#include "compiler.h"
#include "preprocessor.h"
#include "model.h"
#include "datatypes.h"
#include "diagnostics.h"
#include <mpParser.h>
#include <string>
#include <vector>

using namespace std;

typedef rank<int,3> triad;

// print generic warnings during compile stage
#define displaywarning() fprintf(stderr, "Warning in statement beginning on line '%i' in file '%s'.\n", \
    tokens[spos].line_number ,\
    mo.getnamelistname(tokens[spos].namelist_number).c_str());

// ............................................................... //
bool match( const rank4 & r1, const rank4 & r2){
  for(size_t i = 0; i < 4; i++){
    if( r1[i] >= 0 && r2[i] >= 0){
      if(r1[i] != r2[i]) return false;
    }
  }
  return true;
}
// ............................................................... //
// Register local function with the Parser
void reg_func_local(ParserX & P, const char * name, diag_func fptr, const char * sig, bool opt){
  // diag_func is a class that handles the execution of the function
  // sig must have the form  'return type:arg1 type, arg2 type, ...', ex: "c:ii" takes two ints returns a complex type
  rassert(sig != NULL)
    rassert(strlen(sig) >= 2);
  rassert(sig[1] == ':');
  P.DefineFun( new diag_func_class( fptr, name, sig, opt) ); 
};
// ............................................................... //
void assigndiagfuncs(ParserX & P, bool opt){
#define REG_FUNC(NAME, FUNC, SIG) reg_func_local(P, NAME, FUNC, SIG, opt);
#include "diagfunclist.h" // add diagnostic functions to local parser
#undef REG_FUNC
};

//-----------------------------------------------------------------------------//
/* 
   compiler::local Essentially a collection of static functions
   that handle the compilation of the model configuration
   file. These function are defined here in a class 
   because their definition is not needed at the global scope. 

*/
class compiler::local {
  friend class compiler;
  enum blocktype { notype, ifblock, loopblock, runonceblock, forblock };
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  struct control_info {
    stack<int> last_if_pos;
    stack<int> last_if_namelist;
    stack<int> last_if_linenumber;
    stack<bool> last_if_status;
    int size() const
    {
      assert( last_if_pos.size() == last_if_namelist.size() && last_if_namelist.size() == last_if_linenumber.size() &&
          last_if_linenumber.size() == last_if_status.size() );
      return this->last_if_namelist.size();
    }
    void push( int pos, bool status, int ln, int namelist_number )
    {
      last_if_pos.push(pos);
      last_if_namelist.push(namelist_number);
      last_if_linenumber.push(ln);
      last_if_status.push(status);
    };
    void pop()
    {
      last_if_pos.pop();
      last_if_namelist.pop();
      last_if_linenumber.pop();
      last_if_status.pop();
    };
  };
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

  struct diag_compiler {
    model_diagnostics_block * diagblock;
    model_class * moptr;
    const char * fname;
    bool allowimplicit;
    int numargs;
    vector< model_diagnostics_block::instruction * > instructions;    // list of instructions  
    vector< model_diagnostics_block::diagvariable > variables;        // list of current diag variables relevant to the stack
    stack< vector<unsigned int> > variable_scope_resolution_list;

    explicit diag_compiler();

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    bool createblock() {
      this->diagblock->instructions = this->instructions; // assign instruction pointers
      this->diagblock->variables = this->variables; // assign variables
      this->diagblock->attributes.allowimplicit = this->allowimplicit;
      return true; 
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    diag_compiler(model_class * model, model_diagnostics_block * dblock) 
    {
      assert( model != NULL && dblock != NULL );
      this->moptr = model;
      this->diagblock = dblock;
      this->allowimplicit = false;
      this->fname = diagblock -> getfuncname().c_str();
      this->numargs = 0;
    }

    /* compilation interface (implemented beneath here) */
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void addvariable(const string & name, const Value & initval, char_type ct, bool isstatic)
    {
      Value * newval = new Value( initval );
      if ( ct == 'h' ){
          newval->MakeHandleType();
      }else if( ct == 'v' ){
          newval->MakeVariableType();
      }
      this->variables.push_back( model_diagnostics_block::diagvariable(name, newval, ct, isstatic) );
      if( this->variable_scope_resolution_list.size() > 0){
        this->variable_scope_resolution_list.top().push_back( variables.size()-1);
      }
    };
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    bool isvariabledefined(const string & name) const {
      unsigned int tsize = this->variable_scope_resolution_list.size();
      if(tsize == 0){
        for(unsigned int i = 0; i < variables.size(); i++){
          if( variables[i].name == name ){
            return true;
          }
        }
      }else{
        const vector<unsigned int> & varptrlist = variable_scope_resolution_list.top();
        for(unsigned int u = 0; u < varptrlist.size(); u++){
          if( variables[u].name == name ){
            return true;
          }
        }
      }
      return false;
    } 
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void makefunction(const vector< pair<char_type, string> > & arglist, const string & fname, const char_type ret_type)
    {
      diagblock->blocktype = model_diagnostics_block::function;
      this->numargs = diagblock->function_attributes.numargs = static_cast<int>( arglist.size() );
      assert( is_type_valid( ret_type ) );
      Value ival(static_cast<int_type>(0));
      for(int i = 0; i < this->numargs; i++){
        // need to have the right initializer
        switch( arglist[i].first ){
          case 'i': ival = static_cast<int_type>(0); break;
          case 'f': ival = static_cast<float_type>(0); break;
          case 'c': ival = cmplx_type(0,0); break;
          case 'h': ival = static_cast<int_type>(0); 
                    ival.MakeHandleType();
                    break;
          case 'a': ival = vector<Value>(); break;
          case 'b': ival = false; break;
          case 's': ival = string(); break;
          case 'v': ival = static_cast<int_type>(0); 
                    ival.MakeVariableType();
                    break;
          default: fatal_stop("Unrecognized variable type.");
        }
        this->addvariable(arglist[i].second, ival, arglist[i].first, false);  
      }
      diagblock->attributes.name = fname;
      this->fname = diagblock->attributes.name.c_str();
      diagblock->function_attributes.ret_type = ret_type;
    };
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void push_scope(){
      vector<unsigned int> varindexlist; 
      unsigned int tsize = variable_scope_resolution_list.size();
      if(tsize > 0){
        varindexlist =  this->variable_scope_resolution_list.top();
      }else{
        for(unsigned int i = 0; i < this->variables.size(); i++){
          varindexlist.push_back(i);
        }
      }
      this->variable_scope_resolution_list.push(varindexlist);
    };
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void makestatusblock(){
      diagblock->blocktype = model_diagnostics_block::statusblock;
      diagblock->attributes.name = "STATUSSCRIPT";
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void makeendblock(){
      diagblock->blocktype = model_diagnostics_block::endblock;
      diagblock->attributes.name = "ENDSCRIPT";
    };
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void makestartblock(){
      diagblock->blocktype = model_diagnostics_block::startblock;
      diagblock->attributes.name = "ENDSCRIPT";
    };
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void makescript(){
      diagblock->blocktype = model_diagnostics_block::scriptblock;
      diagblock->function_attributes.numargs = 1;
      this->addvariable( "args", Value(static_cast<int_type>(0)), 'i', false );
      diagblock->attributes.name = "SCRIPT";
    };
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void pop_scope(){
      unsigned int tsize = variable_scope_resolution_list.size();
      if(tsize >= 1){
        variable_scope_resolution_list.pop();
        return;
      }
      fatal_stop("Cannot pop variable_scope_resolution because stack is empty.");
    };
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    vector<char_type> GetCallStackTypes() const 
    {
      vector<char_type> ret;
      ret.resize( this->numargs, 0 );
      for(int i = 0; i < this->numargs; i++){
        ret[i] = this->variables[i].dtype;
      }
      return ret;       
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void assigncustomfunctions(ParserX & p){
      model_class & mo = *moptr; // this->getModel();
      // assign all custom functions  
      vector< model_diagnostics_block * > funclist;
      mo.getfunctiondiaglist( funclist );
      for(size_t i = 0; i < funclist.size(); i++){
        model_diagnostics_block * dblock = funclist[i];
        if(dblock->isblockfunction()){
          vector<char_type> cstack;
          if( dblock != this->diagblock ){
            cstack = dblock->GetCallStackTypes();  
          }else{ // var stack has not yet been assigned to the function, so we use the local version
            cstack = this->GetCallStackTypes();  
          }
          p.DefineFun( new user_defined_function( dblock, dblock->GetReturnType(), cstack ) );
        }
      }
    } 
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void assigndiaginfo(ParserX & P){
      model_class & mo = *moptr;
      parser_define_basics(P, &(mo), "v", false, true);
      assigndiagfuncs(P, true);
      this->assigncustomfunctions(P);
      // --- now define the model information
      { // define model parameters
        const size_t psize = mo.get_num_global_params();
        for(size_t i = 0; i < psize; i++){
          const parameter & param = mo.get_global_param_by_index(i);
          if(param.getType() == parameter::Real){
            P.DefineConst( param.GetName(), static_cast<float_type>( param.getfValue()));
          }else if(param.getType() == parameter::Integer){
            P.DefineConst( param.GetName(), static_cast<int_type>( param.getiValue()));
          }else{ // type is a string
            P.DefineConst( param.GetName(), param.getsValue() );
          }
        }
      } // end define model parameters
      { // define model parameters as necessary for the diagnostics block
        for(size_t i = 0; i < mo.get_num_global_profs(); i++){
          const parameter & prof = mo.get_global_prof_by_index(i);
          // Add profile size in profname_size
          if( prof.getType() == parameter::Real ){
            Value arg( prof.GetSize(), static_cast<float_type>(0.0) );
            for(size_t j = 0; j < prof.GetSize(); j++) arg[j] = prof.getData()[j];
            P.DefineConst( prof.GetName(), arg ); 
          }else if( prof.getType() == parameter::Integer ){
            Value arg( prof.GetSize(), static_cast<int_type>(0) );
            for(size_t j = 0; j < prof.GetSize(); j++) arg[j] = prof.getiData()[j];
            P.DefineConst( prof.GetName(), arg ); 
          }else{ // it's a string
            vector<Value> args(prof.GetSize(), Value(""));
            Value arg(args);
            for(size_t j = 0; j < prof.GetSize(); j++) arg[j] = prof.getsData()[j];
            P.DefineConst( prof.GetName(), arg ); 
          }
        }
      } // end define model parameters
      // ---- end define model information 
      { // define the P-system fields and variables
        mo.getPsystem().assignparserdata(P);  
      } // --- end define P-system fields and variables
      { // block define local diag variables 
        const unsigned int tsize = this->variable_scope_resolution_list.size();
        size_t index = 0;
        try{
          if(tsize == 0){    
            for(index = 0; index < this->variables.size(); index++){
              forceDefineVar(P, this->variables[index].name.c_str(), this->variables[index].data);
            }
          }else{
            const vector<unsigned int> & ptrlist = this->variable_scope_resolution_list.top();
            for(size_t i = 0; i < ptrlist.size(); i++){ 
              index = ptrlist[i];
              forceDefineVar(P, this->variables[index].name.c_str(), this->variables[index].data);
            }
          }    
        }catch(const ParserError & e){
          fprintf(stderr, "Error adding variable '%s' to parser.\nError message: '%s'.\n", 
              this->variables[index].name.c_str(),
              e.what());
          throw cgcmexception("Initialization error", -1);
        }
      } // end block define diag variables
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    // check variables on the current stack to ensure they are all
    // assigned valid types by the last instruction ...
    void assert_variable_type_correctness() const
    {
      for(size_t i = 0; i < variables.size(); i++){ 
        variables[i].assert_type_correctness();
      }
    };
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void save_var_list(ParserX & p, vector< pair<string, Value> > & var_list){
      const var_maptype & variables = p.GetVar();
      for(var_maptype::const_iterator item = variables.begin(); item != variables.end(); item++){
        Variable & var = (Variable&)( * item->second );
        const string & name = item->first;
        Value newval(* (var.AsIValue() ) );
        var_list.push_back( pair<string, Value>( name, newval )  );
      }
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void restore_var_list(ParserX & p, vector< pair<string, Value> > & var_list ){
      unsigned int iter = 0;
      unsigned int loopnum = 0;
      // typically the variables  stay in their original order, but iif not we adjust
      var_maptype variables = p.GetVar();
      if(variables.size() > 0 && var_list.size() > 0){
        for(var_maptype::iterator item = variables.begin(); item != variables.end(); item++){
          while( item->first != var_list[iter].first ){
            iter++;
            if(iter >= var_list.size()){
              iter = 0;
              loopnum++;
              if(loopnum > 2) break;
            }
          }
          //fprintf(stderr, "Assign var '%s' value '%f'\n", item->first.c_str(), var_list[iter].second );
          if(loopnum < 3){ // otherwise variable may have been added implicitly and will have no previous value
            Variable * var = dynamic_cast<Variable*>( item->second->AsIValue() );
            Value * var_ptr = dynamic_cast<Value*>(var->GetPtr());
            *var_ptr =  var_list[iter].second ;
          }
          loopnum = 0;
        }
      }
    };
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    int addtrapinstruction() 
    {
      instructions.push_back( new model_diagnostics_block::instruction() );
      model_diagnostics_block::instruction & cinstruct = *( instructions.back() );
      cinstruct.id = model_diagnostics_block::instruction::inst_trap;
      return this->instructions.size() - 1;
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    int addinstruction(const token_list & expr, bool isif, bool isbreakif, bool runonce, bool returninstruction)
    {
      // Implicit declarations should automatically add the variable to the diag_block using the Factory function 
      model_class & mo = *moptr;
      instructions.push_back( new model_diagnostics_block::instruction() );
      Value eval_value; 
      ParserX & P = *(this->instructions.back()->parser); // add predefined variables
      if(this->allowimplicit || mo.get_global_param_by_name(gdb_str_IMPLICIT_DIAG_VARS).getiValue() ){
        P.EnableAutoCreateVar(true);
      }
      assigndiaginfo(P);
      P.SetExpr(expr);
      // first save all the variable values because an evaluation of the parser can change them,
      // even during compilation.
      vector < pair<string, Value> > varlist;
      this->save_var_list(P, varlist);
      try{
        eval_value = P.Eval();
        assert_variable_type_correctness();
        char_type ret_type = diagblock->GetReturnType();
        if( returninstruction && ! does_value_conform_to_type( eval_value, ret_type ) ){
          mpError("Function `%s` must return type %s but instead returned type %s.",
              this->fname, get_type_id(ret_type), get_type_id(eval_value.GetType()));
        }
      }catch(const ParserError & e){
        fprintf(stderr, "%s\n", e.what());
        return -1;
      }
      // restore the variables to their original value
      this->restore_var_list(P, varlist);
      // now add the instruction 
      model_diagnostics_block::instruction & cinstruct = *( instructions.back() );
      cinstruct.runonce = runonce;
      if(isif){
        cinstruct.id = model_diagnostics_block::instruction::inst_jmpnif;
      }else if(isbreakif){
        cinstruct.id = model_diagnostics_block::instruction::inst_jmpif;
      }else if( returninstruction ){
        assert(diagblock->isblockfunction());
        cinstruct.id = model_diagnostics_block::instruction::inst_return;
      }else{
        cinstruct.id = model_diagnostics_block::instruction::inst_mu;
      }
      return this->instructions.size() - 1;
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void addinstructiondebuginfo(const int & namelistindex, const int & slinenumber,
        const int & elinenumber) // adds this to last instruction 
    {
      assert( this->instructions.size() > 0);
      this->instructions.back()->debug_attributes.namelist = namelistindex;
      this->instructions.back()->debug_attributes.slinenumber = slinenumber;
      this->instructions.back()->debug_attributes.elinenumber = elinenumber;
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    int addbreakpointinstruction(bool runonce)
    {
      // now add the new command
      if(instructions.size() > 0){
        this->instructions.back()->debug_attributes.breakpointset = true;
      };
      // else ignore the command
      return this->instructions.size()-1;
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    void assignjmpaddress(const  int & instruction_number, const  int & address)
    {
      rassert(instruction_number >=0 && instruction_number < static_cast<int>(this->instructions.size())
          || this->instructions[instruction_number]->id == model_diagnostics_block::instruction::inst_mu
          || this->instructions[instruction_number]->id == model_diagnostics_block::instruction::inst_exit
          )
        this->instructions[instruction_number]->jmpinstr = address;
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    int addjmpinstruction(bool opt)
    {
      this->instructions.push_back(new model_diagnostics_block::instruction());
      model_diagnostics_block::instruction & instruct = *(instructions.back());
      instruct.id = model_diagnostics_block::instruction::inst_jmp;
      instruct.runonce = opt;
      return this->instructions.size() - 1;
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    const int getinstructionjmpaddress(const  int & instruction_number) const
    {
      rassert(instruction_number >=0 && static_cast<size_t>(instruction_number) < this->instructions.size()
          || this->instructions[instruction_number]->id == model_diagnostics_block::instruction::inst_mu
          || this->instructions[instruction_number]->id == model_diagnostics_block::instruction::inst_exit);
      return this->instructions[instruction_number]->jmpinstr;
    }
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

  }; // local structure that aids in the compilation of a diag block

  static bool iskeywordcontroldirective(const string & keyword) 
  {
    return matchkeyword(keyword,
        gdb_KEYWORD_PREPROC_WARNING,
        gdb_KEYWORD_PREPROC_ERROR ,
        gdb_KEYWORD_PREPROC_MESSAGE ,
        gdb_KEYWORD_PREPROC_ENDIF ,
        gdb_KEYWORD_PREPROC_IF ,
        gdb_KEYWORD_PREPROC_ELSE ,
        gdb_KEYWORD_PREPROC_ELSEIF ,
        gdb_KEYWORD_PREPROC_ENDIF ,
        gdb_KEYWORD_PREPROC_DEFINE ,
        gdb_KEYWORD_PREPROC_UNDEFINE);
  };
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  static void thrownamelisterror(const char * format, const char * namelist, int line_number = -1){
    char * msg = new char[300];
    snprintf(msg, 300, format, namelist, line_number);
    cgcmexception error(msg, -1);
    delete [] msg;
    throw error;
  }
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  static int find_closing_brace(const vector<token> tokens, const int startpos, 
      const string & openbrace, const string & closebrace)
  {
    // startpos should be position of opening brace
    int pos = startpos + 1;
    int open_brace_stack = 0;
    while(pos < static_cast<int>(tokens.size())){
      if(tokens[pos].word == closebrace){
        if( open_brace_stack == 0){
          return pos;
        }else{
          open_brace_stack--;
        }
      }
      if(tokens[pos].word == openbrace) {
        open_brace_stack++;
      }
      pos++;
    }
    return -1;
  }
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  static int get_line_last_pos(const vector<token> & tokens, const int spos, const int epos)
  {
    int rpos = spos;
    while(rpos <= epos){
      if(tokens[rpos].word == ";") return rpos-1;
      rpos++;
    }
    return rpos;
  };
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  static bool go_to_nextblockelement(const vector<token> & tokens, int & pos, const int epos,
      const stack<int> & last_if, const string & keyword, bool endifonly)
  {
    int blockdepth = 0;
    bool foundnextblock = false;
    // const int spos = pos;
    pos++; // march past last element 
    while(pos <= epos){
      if( tokens[pos - 1].line_number < tokens[pos].line_number
          || tokens[pos-1].namelist_number != tokens[pos].namelist_number){ // is this the first token on the line?
        if(tokens[pos].word == gdb_KEYWORD_PREPROC_ENDIF ){
          if(blockdepth > 0){ blockdepth --; }
          else{ foundnextblock = true; break; }
        }else if(tokens[pos].word == gdb_KEYWORD_PREPROC_IF){
          blockdepth++;
        }else if(!endifonly){ // check also for gdb_KEYWORD_PREPROC_ELSE, "ELSIF"
          if(blockdepth == 0){
            if(tokens[pos].word == gdb_KEYWORD_PREPROC_ELSE || tokens[pos].word == gdb_KEYWORD_PREPROC_ELSEIF){
              foundnextblock = true;
              break;
            }
          }
        }
      }
      pos++;
    }
    if(!foundnextblock){
      fprintf(stderr, "Error: Missing '%s' for '%s' statement started on line '%i'.\n", gdb_KEYWORD_PREPROC_ENDIF ,
          keyword.c_str(), last_if.top());
      return false;
    }
    pos -- ; // jump back before block ender so as to preprocess the keyword
    return true;
  }; 
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  /* auxilary function process array block of one dimension */
  static int interpretarray1d(
      const vector<token>& tokens, const int spos, int & pos, vector<token_list> & values, 
      const string & opener, const string & closer
      )
  {
    int depth = 0;
    if(tokens[pos].word != opener){
      fprintf(stderr, "List must be initiated with '%s' on line %i.\n", opener.c_str(), tokens[pos].line_number);
      return -1;
    } pos++;
    values.push_back( token_list() );
    while(true){
      if( tokens[pos].word == closer && depth == 0 ) break;
      if(pos+1 == static_cast<int>(tokens.size()) || tokens[pos].word == ";"){
        fprintf(stderr, "List started on line '%i' must be terminated with '%s' before end of expression.\n",
            tokens[spos].line_number, closer.c_str());
        return -2;
      }
      if( tokens[pos].word == opener ) depth++;
      if( tokens[pos].word == closer ){
        depth--;
        if(depth < 0){
          fprintf(stderr, "Too many '%s' for statement started on line '%i'.\n", closer.c_str(), tokens[spos].line_number);
          return -2;
        }
      }
      if(depth == 0 && tokens[pos].word == ","){
        if(values.back().size() == 0){ //  == ""){
          fprintf(stderr, "Expected expression before ','.\n");
          fprintf(stderr, "Syntax error in list definition beginning on line: %i\n", tokens[spos].line_number);
          return -3;
        }
        values.push_back( token_list() ); //"");
      }else{
        values[values.size()-1].push_back( tokens[pos].word ); //  += tokens[pos].word;
      }
      pos++;
    }
    pos++; // move past final closer
    if(values.back().size() == 0){
      fprintf(stderr, "Expected expression before '%s'.\n", closer.c_str());
      fprintf(stderr, "Syntax error in list definition beginning on line: %i\n", tokens[spos].line_number);
      return -4;
    }
    return 0;				
  };
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  static int interpretline(const vector<token>& tokens, 
      const int spos, const int epos, int & pos, vector<token_list> & values, const string & opener, 
      const string & closer)
  {
    if(pos > epos || tokens[pos].word != "="){
      fprintf(stderr, "Assignment requires '=' operator after '%s' on line: %i.\n", 
          tokens[pos-1].word.c_str(),tokens[pos-1].line_number);
      return -2;
    }
    pos++;
    stack<char> groupers;
    enum rmode { VALUE, ARRAY };
    rmode readmode = VALUE;
    int cpos = -1;
    while(pos <= epos){
      if(readmode == VALUE){
        if(tokens[pos].word == opener){
          readmode = ARRAY;
          // make sure there is a closing brace
          if(values.size() != 0){ // assignment was already made
            fprintf(stderr, "Syntax error in array definition on line: %i\n", tokens[pos].line_number);
            return -4;
          }        
          cpos = find_closing_brace(tokens, pos, opener, closer);
          if(cpos < 0 || cpos > epos){
            fprintf(stderr, "Missing closing '%s' begun on line: %i.\n", closer.c_str(), tokens[pos].line_number);
            return -3;
          }
          values.push_back(token_list());
        }else{ // attempt to read the value
          if(values.size() == 0){
            values.push_back(token_list(1,tokens[pos].word));
          }else{
            // we will process the expression using the mpParser
            preprocessor::combinetokens(tokens, values[0], pos, pos);
          }
        }
      }else if(readmode == ARRAY){
        if(tokens[pos].word == "("){
          groupers.push('(');
          preprocessor::combinetokens(tokens, values[values.size() - 1], pos, pos );
        }else if( tokens[pos].word == ")" && groupers.size() > 0 ){
          groupers.pop();
          preprocessor::combinetokens(tokens, values[values.size() - 1], pos, pos );
        }else if(tokens[pos].word == "," && groupers.size() == 0){
          if(values[values.size()-1].size() == 0){ // == ""){
            fprintf(stderr, "Expected expression before ','.\n");
            fprintf(stderr, "Syntax error in array definition on line: %i\n", tokens[pos].line_number);
            return -4;
          }    
          values.push_back(token_list()); // another expression is expected
          }else if(tokens[pos].word == closer && pos >= cpos){
            if(values[values.size()-1].size() == 0 ){ // ==""){
              fprintf(stderr, "Syntax error in array definition on line: %i.\n%s\n", tokens[pos].line_number,
                  "Not enough elements in array.\n");
              return -4;
            }
            readmode = VALUE;
            }else{ // we have some other token, concatenate
              preprocessor::combinetokens(tokens, values[values.size() - 1], pos, pos);
            }
          }
          pos++;
        }
        return 0;
      };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static int interpretangularmodes(model_class & mo, const vector<token> & tokens, 
          int & pos, initialdata::rule & idrule, initialdata & initial_conditions)
      {
        const int spos = pos;
        vector<token_list> args;
        if(interpretarray1d(tokens, pos, pos, args, "<", ">") < 0){ return -1; }
        // make sure we are defining a fourier_angular rule 
        if(args.size() > 1){
          fprintf(stderr, "Error: Angular mode index list takes a max of 1 argument.\n");
          fprintf(stderr, "Error on line %i.\n", tokens[pos].line_number);
          return -2;
        }
        ParserX & eval = idrule.parser; // parser rule
        bool free_index = false; // is the index free or evaluated
        Value & index_mode = initial_conditions.index_mode;
        //Value & mode_val = initial_conditions.modeval;
        rassert( args.size() == 1 );
        for(size_t i = 0; i < args.size(); i++){
          if( isVariableLegal( convert_expr_to_string(args[i]).c_str() ) ){ // if this is a legal variable name, check to see if 
            // it is already defined otherwise it represents a free index
            if(isTokenDefined( args[i].back(), eval)){
              fprintf(stderr, "Warning: Token %s has prior definition to statement beginning on line %i.\n", 
                  convert_expr_to_string(args[i]).c_str(), tokens[spos].line_number);
              fprintf(stderr, "         Angular mode will be evaluated and not treated as a free index.\n");
              free_index = false;
            }else{
              free_index = true;
            }
          }else{
            // evaluate the expression 
            free_index = false;
          }
          if( ! free_index ){
            eval.SetExpr( args[i] );
            const IValue & val = eval.Eval();
            if( ! val.IsInteger() ){ // make sure expression evaluated to an integer
              fprintf(stderr, 
                  "Error: Domain index argument must evaluate to an integer but instead evaluated to %s.\n", 
                  cast_to_string(val).c_str());
              fprintf(stderr, "Error in element %i of domain index list.\n", static_cast<int>(i)+1);
              fprintf(stderr, "Error in domain index list argument on line %i.\n", tokens[pos-1].line_number);
              return -8;
            }
            idrule.rpos[0] = static_cast<INDEX>( val.GetInteger() );
          }else{ // define a variable
            assert( args[i].size() == 1 );
            const string & varName = args[i].back();
            eval.DefineVar( varName , &index_mode );
            idrule.rpos[0] = -1;
          } 
        }
        return 0;
      }
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static int interpretdomainarguments(model_class & mo, const vector<token> & tokens, 
          int & pos, initialdata::rule & idrule, initialdata & initial_conditions)
      {
        const int spos = pos;
        vector<token_list> args;
        if(interpretarray1d(tokens, pos, pos, args, "(", ")") < 0){ return -1; }
        const int numargs = 1;
        if( static_cast<int>(args.size()) > numargs){
          fprintf(stderr, "Error:  Definition takes at most %i domain arguments but %i were supplied.\n",
              numargs, static_cast<int>(args.size()));
          fprintf(stderr, "Error in field domain list assignment on line %i.\n", tokens[pos-1].line_number);
          return -4;
        }
        ParserX & eval = idrule.parser; // parser rule
        bool free_index = false; // is the index free or evaluated
        Value & index_k = initial_conditions.index_k;
        for(size_t i = 0; i < args.size(); i++){
          if( isVariableLegal( convert_expr_to_string(args[i]).c_str() ) ){ // if this is a legal variable name, check to see if 
            // it is already defined otherwise it represents a free index
            assert( args[i].size() == 1 );
            if(isTokenDefined(args[i].back(), eval)){
              fprintf(stderr, "Warning: Token %s has prior definition to the statement beginning on line %i.\n", 
                  convert_expr_to_string(args[i]).c_str(), tokens[spos].line_number);
              fprintf(stderr, "         k-wavenumber mode will be evaluated and not treated as a free index.\n");
              free_index = false;
            }else{
              free_index = true;
            }
          }else{
            // evaluate the expression 
            free_index = false;
          }
          if( ! free_index ){
            eval.SetExpr( args[i] );
            const IValue & val =  eval.Eval();
            if( ! val.IsInteger() ){ // make sure expression evaluated to an integer
              fprintf(stderr, "Error: k-wavenumber index argument must evaluate to an integer but evaluated to %s.\n", 
                  cast_to_string(val).c_str());
              fprintf(stderr, "Error in element %i of domain index list.\n", static_cast<int>(i)+1);
              fprintf(stderr, "Error in domain index list argument on line %i.\n", tokens[pos-1].line_number);
              return -8;
            }
            idrule.rpos[1] = static_cast<INDEX>(val.GetInteger() - 1);
          }else{ // define a variable
            assert( args[i].size() == 1 );
            eval.DefineVar( args[i].back(), &index_k );
            idrule.rpos[1] = -1;
          } 
        }
        return 0;
      };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static int interpretfieldindices(model_class & mo, const vector<token> & tokens, 
          int & pos, initialdata::rule & idrule, initialdata & initial_conditions)
      {
        const int spos = pos;
        int num_fields = mo.get_global_param_by_name(gdb_str_NUMFIELDS).getiValue();
        vector<token_list> args;
        if(interpretarray1d(tokens, pos, pos, args, "[", "]") < 0){ return -1; }
        if( args.size() != 2){
          fprintf(stderr, "Error: Phi takes 2 field index arguments but %i were supplied.\n", 
              static_cast<int>(args.size()));
          fprintf(stderr, "Error in field index list assignment on line %i.\n", tokens[pos-1].line_number);
          return -4;
        }
        ParserX & eval = idrule.parser; // parser rule
        for(size_t i = 0; i < args.size(); i++){
          bool free_index = false; // is the index free or evaluated
          Value & field_index = (i==0) ? initial_conditions.index_i : initial_conditions.index_j;
          if( isVariableLegal( convert_expr_to_string(args[i]).c_str() ) ){ // if this is a legal variable name, check to see if 
            // it is already defined otherwise it represents a free index
            rassert( args[i].size() == 1 );
            if(isTokenDefined(args[i].back(), eval)){
              fprintf(stderr, "Warning: Token %s has a prior definition to the statement beginning on line %i.\n", 
                  convert_expr_to_string(args[i]).c_str(),tokens[spos].line_number);
              fprintf(stderr, "         field index identifier '%i' will be evaluated and not treated as a free index.\n", static_cast<int>(i+1));
              free_index = false;
            }else{
              free_index = true;
            }
          }else{
            // evaluate the expression 
            free_index = false;
          }
          if( ! free_index ){
            eval.SetExpr( args[i] );
            const IValue & val =  eval.Eval();
            if( ! val.IsInteger() ){ // make sure expression evaluated to an integer
              fprintf(stderr, "Error: field index argument %i must evaluate to an integer but evaluated to %s.\n",
                  static_cast<int>(i+1),  
                  cast_to_string(val).c_str());
              fprintf(stderr, "Error in element %i of domain index list.\n", static_cast<int>(i)+1);
              fprintf(stderr, "Error in domain index list argument on line %i.\n", tokens[pos-1].line_number);
              return -8;
            }
            // make sure we are within the limits
            int ival = static_cast<int>( val.GetInteger() );
            if( ival <= 0 || ival > num_fields ){
              fprintf(stderr, "Error: field index argument %i must evaluate to an integer between 1 and %i.\n",
                  static_cast<int>(i+1), num_fields);
              fprintf(stderr, "Error in domain index list argument on line %i.\n", tokens[pos-1].line_number);
              return -8;
            }
            idrule.rpos[2+i] = static_cast< INDEX > (ival - 1);
          }else{ // define a variable
            rassert( args[i].size() == 1 );
            eval.DefineVar( args[i].back(), &field_index );
            idrule.rpos[2+i] = -1;
          } 
        }
        return 0;
      }
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static void assignmdata_aux(const string & expr, const rank4 & curpos, map<rank4, string> & exprs)
      {
        bool foundpairs = false;
        for(std::map<rank4, string>::iterator i = exprs.begin(); i != exprs.end(); i++){
          if( match((*i).first , curpos) ){
            foundpairs = true;
            (*i).second += expr;
          }
        }
        if(!foundpairs){ // push curpos on the stack without free indices!!
          rank4 spos(curpos);
          if(spos[3] < 0){ spos[3] = 1; };
          if(spos[2] < 0){ spos[2] = 1; };
          if(spos[1] < 0){ spos[1] = 1; };
          if(spos[0] < 0){ spos[0] = 1; };
          exprs.insert( pair<rank4, string>(spos, expr) );
        }
      }
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static int checkcurexpr(const vector<token> & tokens,
          const int line_number, token_list & curexpr, initialdata::rule & idrule, 
          INDEX ** poslist, int maxpos)
      {
        // attempt to parse expression and check syntax
        if(curexpr.size() == 0) return 0;
        ParserX & peval = idrule.parser;
        peval.SetExpr(curexpr);
        try{
          //double r = 
          peval.Eval();
        }catch(const ParserError & e){
          fprintf(stderr, "%s\n", e.what() ); 
          fprintf(stderr, "Error in expression defining array elements: ");
          for(int i = 0; i < maxpos; i++){
            if(*(poslist[i]) >= 0){
              fprintf(stderr, "%i ", (*(poslist[i])));
            }else{
              fprintf(stderr, "* ");
            }
          }
          fprintf(stderr, "\n");
          fprintf(stderr, "Error on line %i.\n", line_number);
          return -1;
        }
        curexpr.clear(); 
        return 0;
      };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static void pushOPstring(vector< vector< token_list > > & opstrings, 
          int index_i, int index_j, const string & el)
      {
        const unsigned int & num_fields = opstrings.size();
        if(index_i == 0 && index_j == 0){ // apply to all elements i,j
          for(unsigned int i = 0; i < num_fields; i++){
            for(unsigned int j = 0; j < num_fields; j++){
              opstrings[i][j].push_back(el); //+= el;
            }
          }
        }else if(index_i == 0){ // apply to all elements
          unsigned int j = index_j - 1;
          for(unsigned int i = 0; i < num_fields; i++){
            opstrings[i][j].push_back(el); //+= el;
          }
        }else if(index_j == 0){ // apply to all j elements for index_i
          unsigned int i = index_i - 1;
          for(unsigned int j = 0; j < num_fields; j++){
            opstrings[i][j].push_back(el); //+= el;
          }
        }else{ // apply to a single element index_i, index_j
          unsigned int i = index_i - 1;
          unsigned int j = index_j - 1;
          opstrings[i][j].push_back(el); //+= el;
        } 
      };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool checkOPstringsyntax(vector< vector< token_list > > & opstrings, ParserX & P)
      {
        const size_t num_fields = opstrings.size();
        for(size_t i = 0; i < num_fields; i++){
          for(size_t j = 0; j < num_fields; j++){
            const token_list & expr = opstrings[i][j];
            if(expr.size() > 0 ){
              P.SetExpr(expr);
              try{
                P.Eval();
              }catch(const ParserError & e){
                fprintf(stderr, "Error: %s\n", e.what());
                fprintf(stderr, "Error in element [i=%i,j=%i] which translates to '%s'.\n", 
                    static_cast<int>(i), static_cast<int>(j), convert_token_list_to_string(expr).c_str());
                return false;
              }
            }
          }}
        return true;
      };
#ifdef DEBUG
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static void dumpOPstring(const linearOPtranslationdata & opdata)
      {
        FILE * rout = getrout();
        fprintf(rout, "Printing OP: '%s'.\n", opdata.OPname.c_str());
        fprintf(rout, "----------------------------------------------------\n{\n");
        for(size_t u = 0; u < opdata.expressions.size(); u++){
          const rank3 & pos = opdata.expressions[u].first;
          const token_list & val = opdata.expressions[u].second;
          fprintf(rout, "nki = %i, i=%i, j=%i --> [ %s ]\n",
              static_cast<int>( pos[0] ),
              static_cast<int>( pos[1] ),
              static_cast<int>( pos[2] ),
              convert_expr_to_string(val).c_str()
              );
        }
      }
#endif
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static void filter_arg_sequences(token_list & args)
      {
        for(size_t wordIter = 0; wordIter < args.size(); wordIter++){
          string & output = args[wordIter];
          string input(output);
          int i = 0, j = 0;
          for( ; i < input.size(); j++, i++){
            if( i < input.size() - 1 && input[i] == '\\' && input[i+1] == 'n' ){
              output[j] = '\n';
              i++;
            }else{
              output[j] = input[i];
            }
          }
          output[j] = 0;
        }
      }
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#define directive_error(fmt,...) { fprintf(stderr, fmt, ## __VA_ARGS__); \
  fprintf(stderr, "Error in '%s' directive starting on line %i.\n", keyword.c_str(), tokens[spos].line_number);\
  return false; };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processcontroldirective(model_class & mo, const vector<token> & tokens, int & pos, const int epos, control_info & if_data)
      {
        const int spos = pos;
        const string & keyword = tokens[spos].word;
        const char * namelist = mo.getnamelistname(tokens[spos].namelist_number).c_str();
        const stack<int> & last_if = if_data.last_if_linenumber;
        if( matchkeyword(keyword, gdb_KEYWORD_PREPROC_WARNING, gdb_KEYWORD_PREPROC_ERROR, gdb_KEYWORD_PREPROC_MESSAGE) )
        {
          token_list pexpr;
          preprocessor::get_preproc_arg(mo, pexpr, tokens,  pos,  true); 
          filter_arg_sequences(pexpr);
          if(pexpr.size() == 0) directive_error("Error: No display message provided to '%s' directive.\n", keyword.c_str());
          if(keyword == gdb_KEYWORD_PREPROC_ERROR){ // specially handler error message ...
            fprintf(stderr, "Error: %s\n", convert_token_list_to_string(pexpr).c_str());
            fprintf(stderr, "User defined preprocessor error on line %i.\n", tokens[spos].line_number);
            return false;
          }else if(keyword == gdb_KEYWORD_PREPROC_WARNING){
            fprintf(stderr, "Warning: ");
          }
          fprintf(stderr, "%s\n", convert_token_list_to_string(pexpr).c_str());
        }else if( keyword == gdb_KEYWORD_PREPROC_ENDIF){
          if(if_data.size() == 0) 
            directive_error("Error: Encountered '%s' without '%s' on line '%i'.\n", 
                gdb_KEYWORD_PREPROC_ENDIF, gdb_KEYWORD_PREPROC_IF, tokens[pos].line_number);
          if_data.pop();
        }else if( keyword == gdb_KEYWORD_PREPROC_ELSE){
          if(if_data.size() == 0) directive_error("Error: Encountered '%s' without '%s' on line '%i'.\n",
              gdb_KEYWORD_PREPROC_ELSE, gdb_KEYWORD_PREPROC_IF, tokens[pos].line_number);
          if(if_data.last_if_status.top()){
            if(!go_to_nextblockelement(tokens, pos, epos, last_if, tokens[pos].word, true)){
              return false;
            };
          }
        }else if(matchkeyword( keyword, gdb_KEYWORD_PREPROC_IF, gdb_KEYWORD_PREPROC_ELSEIF)){
          token_list pexpr;
          preprocessor::get_preproc_arg(mo, pexpr, tokens,  pos,  false); 
          if(keyword == gdb_KEYWORD_PREPROC_ELSEIF  && if_data.size() == 0)
            directive_error("Error: Encountered '%s' without '%s' on line '%i'.", gdb_KEYWORD_PREPROC_ELSEIF,  
                gdb_KEYWORD_PREPROC_IF, tokens[pos].line_number);
          if(keyword == gdb_KEYWORD_PREPROC_IF){
            if_data.push(spos, false, tokens[spos].line_number, tokens[spos].namelist_number);
          }
          if(keyword == gdb_KEYWORD_PREPROC_ELSEIF && if_data.last_if_status.top()){ // go to the next gdb_KEYWORD_PREPROC_ENDIF 
            if(!go_to_nextblockelement(tokens, pos, epos, last_if, keyword, true))
              directive_error("Error in control in file '%s' at statement beginning on line '%i'.", namelist, tokens[spos].line_number);
          }else{
            bool defined = false;
            ParserX  Pcheck(pckNONE); // we assign a factory function that sets the value 
            // of an undefined variable to 1 if defined and 0 if undefined
            mo.assignparserdata(Pcheck, "v", false); //assignparserdatawithmacros( mo, Pcheck, mo.getPsystem() );
            Pcheck.SetExpr(pexpr);
            try{  
              defined = cast_to_bool(Pcheck.Eval());
            }catch(const ParserError & e){
              directive_error("Error in conditional directive expression; '%s'\n%s", e.what(),
                  "Perhaps you forgot a semicolon at the end of the statement?");
            }
            if( matchkeyword(keyword, gdb_KEYWORD_PREPROC_IF,gdb_KEYWORD_PREPROC_ELSEIF) && ( defined==true ) ) {
              if_data.last_if_status.top() = true;
            }
            if(!if_data.last_if_status.top()){ // if this is true then we process the current block, 
              // otherwise we move to the beginning of the next block
              // else, jump to the next block
              if(!go_to_nextblockelement(tokens, pos, epos, last_if, keyword, false)){
                return false;
              }
            }
          }
        }else if(keyword == gdb_KEYWORD_PREPROC_DEFINE){
          pos++;
          if(pos > epos || tokens[pos - 1].line_number != tokens[pos].line_number || tokens[pos-1].namelist_number !=
              tokens[pos].namelist_number)
            directive_error("Error in '%s' statement on line %i.\n%s", keyword.c_str(), tokens[pos].line_number,"Statement expects a token name.");
          const string & mname = tokens[pos].word; 
          token_list pexpr;
          preprocessor::get_preproc_arg(mo, pexpr, tokens,  pos,false);
          // it's OK if the string is empty here
          // check variable name
          if(!isVariableLegal(mname.c_str())){
            directive_error("Macro name '%s' is illegal.\n", mname.c_str());
          }
          // macros can be defined as long as there is no other macro already defined by that name
          if(mo.preprocdata.doesmacroexist(mname)){
            directive_error("Macro named '%s' has already been defined.\n", mname.c_str());
          }
          mo.definemacro(mname, pexpr);
        }else if(keyword == gdb_KEYWORD_PREPROC_UNDEFINE){
          token_list pexpr;
          preprocessor::get_preproc_arg(mo, pexpr, tokens,  pos, false);
          if(pexpr.size() > 0){
            for(size_t i = 0; i < pexpr.size(); i++){
              if(mo.preprocdata.doesmacroexist(pexpr[i])){
                mo.undefinemacro(pexpr[i]);
              }else{
                directive_error("Error: Macro '%s' cannot be undefined because it was not previously defined.\n", pexpr[i].c_str());
              }
            }
          }
        }
        return true;
      };
#undef directive_error
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#define line_error(fmt,...) { fprintf(stderr, fmt, ## __VA_ARGS__); \
  fprintf(stderr, "Error in statement starting on line %i of file '%s'.\n", tokens[spos].line_number, \
      mo.getnamelistname(tokens[pos].namelist_number).c_str());\
  return false; };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processdataline(model_class & mo, const vector<token>& tokens, const int spos, const int epos){
        // first keyword in each line must be a parameter or profile name
        // this function is reconfigured to only process a single line at this point since all blocks but the
        // diagnostic block are being consolidated for macro substitution ease.
        int pos = spos;
        bool nogrouper = true;
        while(pos <= epos){
          // each line must have the form 
          //              variable = value;
          // or           variable = { val1, val2, val3, val4, val5, val6, val7, .... };
          int varname_linenumber = tokens[pos].line_number; // line number where variable name is located
          const char * varname = tokens[pos].word.c_str(); // store the name of the variable being assigned
          vector<token_list> values;  // original tokens
          pos++;
          // here we check if the array happens to be an incremental type which is denoted by [ min, max, factor, steptype ];
          string opener;
          string closer;
          if(pos +1 <= epos){
            if(tokens[pos+1].word == "["){ opener = "["; closer = "]"; }
            if(tokens[pos+1].word == "{"){ opener = "{"; closer = "}"; }
          }
          if( tokens[pos+1].word == opener ) nogrouper = false;
          // next must be an equal sign
          if(interpretline(tokens, spos, epos, pos, values, opener, closer) < 0) return false;
          if(values.size() <= 0){
            line_error( "Expected statement after assignment operator on line %i, following '%s'.\n",
                varname_linenumber, varname);
          }
          int prof_index = mo.get_global_prof_index_from_name(varname);
          int param_index= mo.get_global_param_index_from_name(varname);

          if(prof_index == -1 && param_index == -1){
            line_error( "Unrecognized model parameter or variable '%s' at line %i.\n", varname, varname_linenumber);
          }else if( (prof_index >= 0 && param_index == -1) || (prof_index >= 0 && values.size() > 1) ){
            // assign to profile
            // this second conditions will assign an array to a profile
            // condition 1: prof_index >= 0 --> profile with name varname exists in model 
            //              param_index == -1 --> param with name varname does not exist in model
            // condition 2: profile with name varname exists in model 
            //              an array was declared with 2 or more elements, so even if a param exists
            //              with the given name we assign the array to a profile
            if(mo.assignprof(prof_index, values, opener == "[", opener.empty()) < 0){
              line_error( "Error assigning value to variable '%s' on line %i.\n",
                  varname, varname_linenumber);
            }
          }else if( (param_index >= 0 && prof_index < 0) ){
            // assign to parameter
            if(values.size() > 1){
              line_error("Variable '%s' takes only 1 element. Overloaded assignment at line: %i.\n",
                  varname, varname_linenumber);
            }
            if(mo.assignparam(param_index, values[0]) < 0){
              line_error( "Error assigning value to variable '%s' on line %i.\n",
                  varname, varname_linenumber);
              return -6;
            }
          }else{
            // ambiguous definition (i.e. a varname exists that is both a profile and parameter
            // in the model, and the size given was 1 element, so we cannot determine whether or
            // not the user wants to set the array or the parameter
            line_error( "Ambiguous assignment of variable '%s' with 1 element at line %i.\n%s\n",
                varname, varname_linenumber,
                "Cannot determine assignment intention.\n");
          }
          pos++;
        }
        if(pos > epos){
          pos = epos;
        }
        return true;
      }
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processdeclare(model_class & mo, const vector<token> & tokens, int & pos, const int epos){
        const int spos = pos;
        const string & directivename = tokens[pos].word;
        string var_type;
        vector<string> var_names;
        var_names.push_back("");
        pos++; // march past HEADER directive
        while(tokens[pos].word != ";"){
          if(var_type == ""){
            var_type = tokens[pos].word;
          }else{ // we either expect a name or a comma 
            if(var_names[var_names.size()-1] == ""){
              if(tokens[pos].word == ","){ // wrong, we need a name
                line_error("Error: '%s' statement expects a parameter name at position '%i'.\n", directivename.c_str(),
                    static_cast<int>(var_names.size()) - 1);
              }
              var_names[var_names.size()-1] = tokens[pos].word;
            }else{ // we expect a comma
              if(tokens[pos].word != ","){ // error
                line_error("Error: Too many arguments following '%s' statement.\n", directivename.c_str());
              }
              var_names.push_back("");
            }
          }
          pos++;
        }
        // finished reading declaration list, now define the variables
        for(size_t iter = 0; iter < var_names.size(); iter++){
          // check to make sure all var_names were assigned a name
          if(var_names[iter] == ""){
            line_error("Error: '%s' statement expects a parameter name in position '%i'.\n",
                directivename.c_str(), static_cast<int>(iter));
          }
          // check if variable names are legal
          if( !isVariableLegal(var_names[iter].c_str()) ){
            line_error("Error: Parameter name '%s' in position '%i' is not a legal name.\n", var_names[iter].c_str(),
                static_cast<int>(iter));
          }
          // now check if parameter by that name already exists
          bool exists = false;
          try {	
            //const parameter & param =
            mo.get_global_param_by_name(var_names[iter].c_str());
            exists = true;
          }catch(const cgcmexception & e){
            exists = false;
          }
          if(!exists){ 
            try { 
              // const parameter & prof = 
              mo.get_global_prof_by_name(var_names[iter].c_str());
              exists = true;
            }catch(cgcmexception e){
              exists = false;
            }
          }
          if(exists){
            line_error("Error: Parameter named '%s' already exists and cannot be redeclared.\n", var_names[iter].c_str());
          }else{ // create the new parameter and or profile
            if(strcmp(var_type.c_str(), "PROFILE")==0){
              parameter * newprof =  new parameter(var_names[iter].c_str(), static_cast<REAL>(0));
              mo.addprofile(newprof);     
            }else{
              parameter * new_param = NULL;
              if(strcmp(var_type.c_str(), "REAL") == 0){
                REAL f = 0.0;
                new_param = new parameter(var_names[iter].c_str(), f);
              }else if(strcmp(var_type.c_str(), "STRING") == 0){
                const char * c = "";
                new_param = new parameter(var_names[iter].c_str(), c);
              }else if(strcmp(var_type.c_str(),"INTEGER") == 0){
                int i = 0;
                new_param = new parameter(var_names[iter].c_str(), i);
              }else{
                fprintf(stderr, "Error: Cannot declare constant of unknown type '%s'.\n", var_type.c_str());
                line_error("Error in position '%i' of '%s' statement.\n", static_cast<int>(iter), directivename.c_str());
              }
              mo.GLOBAL_PARAMETERS.push_back(new_param);
            }
          }
        }  // end the for loop iterating over all declared variables
        return true; // everything worked out OK
      }
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processlinearopblock(model_class & mo, const vector<token> & tokens, const int spos, 
          const int epos, spreprocdata & spdata, const int op_pos)
      {
        const size_t num_fields = static_cast<size_t>(mo.get_global_param_by_name(gdb_str_NUMFIELDS).getiValue());
        int cur_i = 0; // when 0 it means all i
        int cur_j = 0; // when 0 it means all j
        stack<size_t> matrix_nesting; // stores the number of elements stored within the current matrix block
        // a max depth of 2 is allowed, and a max number of elements of 6 is allowed
        //int complexdepth = 0;
        stack<char> grouping_ops; // keeps track of groups like (, {, [ etc to make sure there is no
        // mismatching.

        int pos = spos;
        linearOPtranslationdata & opdata = spdata.OPdata[op_pos];
        //kspaceOPstring & opstrings = spdata.opstrings_kspace.back();
        // const string & opname = opdata.OPname;

        token_list texpr; // store matrix elements for parser to make sure they parse correctly at that stage

        ParserX P(pckNONE); // used just for syntax checking
        mo.assignparserdata(P, "if", false);
        mo.assigndiagfunctions(P);
        Value  m = static_cast<int_type>(1);
        Value  k = static_cast<float_type>(1);
        Value  i = static_cast<int_type>(1);
        Value  j = static_cast<int_type>(1);

        forceDefineVar(P, "k", &k);
        forceDefineVar(P, "m", &m);
        forceDefineVar(P, "i", &i);
        forceDefineVar(P, "j", &j);

        vector< vector<token_list> > opstrings;
        token_list initval;
        opstrings.resize( num_fields, vector<token_list>(num_fields, initval) ); 

        while(pos <= epos){
          if(tokens[pos].word == "{"){
            if(matrix_nesting.size() == 2){
              fprintf(stderr, "Error: Only 2-dimensional matrices are allowed in operator definitions.\n");
              line_error("Too many '{' on line %i.\n", tokens[pos].line_number);
            }
            grouping_ops.push('{');
            // first push a parenthesis
            pushOPstring(opstrings, cur_i, cur_j, string("("));
            matrix_nesting.push(1);
            if(cur_i == 0){ 
              cur_i = 1; 
            }else{
              if(cur_j == 0){ 
                cur_j = 1; 
              }
            }
          }else if(tokens[pos].word == "}"){
            if(grouping_ops.empty()){
              fprintf(stderr, "Error: Unexpected '}' found on line %i.\n", tokens[pos].line_number);
              line_error("       No opening '{' was encountered previously.\n");
            }
            char c = grouping_ops.top(); 
            grouping_ops.pop();
            if(c != '{'){
              fprintf(stderr, "Error: Unexpected '}' found on line %i.\n", tokens[pos].line_number);
              line_error("       Expecting a close to '%c'.\n", c);
            }
            if(matrix_nesting.size() > 0){
              if(matrix_nesting.size() == 1 && matrix_nesting.top() < num_fields){
                fprintf(stderr, "Error: Not enough rows were defined in matrix. %i missing.\n", num_fields-static_cast<int>(matrix_nesting.top()));
                line_error("Error near line %i.\n", tokens[pos].line_number);
              }
              if(matrix_nesting.size() == 2 && matrix_nesting.top() < num_fields){
                fprintf(stderr, "Error: Not all field elements were defined in matrix. %i missing.\n", num_fields-static_cast<int>(matrix_nesting.top()));
                string wexpr;
                int bmin = pos - spos < 8 ? pos-spos : 8;
                for(int i = bmin; i > 0; i--){
                  wexpr+=tokens[pos-i].word;
                }
                line_error("Error on line %i following expression %s.\n", tokens[pos].line_number, wexpr.c_str());
              }
            }else{
              line_error("Error: Unmatched '}' found on line %i.\n", tokens[pos].line_number);
            }
            if(matrix_nesting.size() == 2){
              P.SetExpr(texpr);
              try{
                //double val = 
                P.Eval();
              }catch(const ParserError & e){
                fprintf(stderr, "Error: %s\n", e.what());
                fprintf(stderr, "Parsing of expression '%s' failed.\n", convert_expr_to_string(texpr).c_str());
                line_error("Error in matrix element [i=%i,j=%i] on line %i.\n", cur_i, cur_j, tokens[pos].line_number);
              }
              texpr.clear();
              cur_j = 0;
            }
            if(matrix_nesting.size() == 1){
              cur_i = 0;
            }
            pushOPstring(opstrings, cur_i, cur_j, string(")"));
            matrix_nesting.pop();
          }else if(tokens[pos].word == "("){
            grouping_ops.push('(');
            pushOPstring(opstrings, cur_i, cur_j, tokens[pos].word);
          }else if(tokens[pos].word == ")"){
            if(grouping_ops.empty()){
              fprintf(stderr, "Error: Unexpected ')' found on line %i.\n", tokens[pos].line_number);
              line_error("       No opening '(' was encountered previously.\n");
            }
            char c = grouping_ops.top();
            grouping_ops.pop();
            if(c != '('){
              fprintf(stderr, "Error: Unexpected ')' found on line %i.\n", tokens[pos].line_number);
              line_error("       Expecting a close to '%c'.\n", c);
            }
            pushOPstring(opstrings, cur_i, cur_j, tokens[pos].word);
          }else if(tokens[pos].word == ","){
            if(grouping_ops.top() == '{'){
              matrix_nesting.top() = matrix_nesting.top()+1;
              if(matrix_nesting.top() > num_fields){
                fprintf(stderr, "Error: Too many elements in matrix on line %i.\n", tokens[pos].line_number);
                line_error("A max of %i elements is allowed per row and column.\n", static_cast<int>(num_fields));
              }
              if(matrix_nesting.size() == 1){ cur_i++; };
              if(matrix_nesting.size() == 2){ 
                // test texpr to make sure it parses correctly
                P.SetExpr(texpr);
                try{
                  //double val = 
                  P.Eval();
                }catch(const ParserError & e){
                  fprintf(stderr, "Error: %s\n", e.what());
                  line_error("Error in matrix element [i=%i,j=%i] on line %i.\n", cur_i, cur_j, tokens[pos].line_number);
                }
                texpr.clear();
                cur_j++; 
              };
            }else if(grouping_ops.top() == '('){ // could be inside a ParserX function, ignore
            }else{
              line_error("Error: ',' has no meaning outside an array operator or function.\n");
            }
          }else{
            // else we have an expression that needs to be concatenated
            // check if the token is an existing linearOP or component is an existing operator
            bool isop = false;
            for(size_t i = 0; i < spdata.OPdata.size()-1; i++){ // don't include the current operator
              if(tokens[pos].word == spdata.OPdata[i].OPname && spdata.OPdata[i].a_mode == opdata.a_mode){
                linearOPtranslationdata & included_op = spdata.OPdata[i];
                isop = true;
                if(matrix_nesting.size() == 0 ){
                  // changing what is written here because it did not make sense what was being done before
                  for(size_t iter = 0; iter < included_op.expressions.size(); iter++){
                    const rank3 & rpos = included_op.expressions[iter].first;
                    int a = static_cast<int>(rpos[1])+1;
                    int b = static_cast<int>(rpos[2])+1;
                    const token_list & expr = included_op.expressions[iter].second;
                    for(size_t u = 0; u < expr.size(); u++){
                      pushOPstring(opstrings, a, b, expr[u]);
                    }
                  }
                }else{
                  line_error("Error: Cannot reference a linear operator '%s' within an array.\n", 
                      tokens[pos].word.c_str());
                }
              }
            }
            if(!isop){
              pushOPstring(opstrings, cur_i, cur_j, tokens[pos].word);
            }
          }
          if(matrix_nesting.size() == 2){ 
            if( ! ( ( tokens[pos].word == "{" || tokens[pos].word == ",") && texpr.size() == 0)){
              texpr.push_back( tokens[pos].word ); // += tokens[pos].word; 
            }
          };
          pos++;
        }
        if(!grouping_ops.empty()){
          char c = grouping_ops.top();
          line_error("Error: Missing closing for '%c' on line %i.\n", c, tokens[pos].line_number);
        }

        if(!checkOPstringsyntax(opstrings, P)){
          line_error("Error in linear operator definition beginning on line %i.\n", tokens[spos].line_number);
        }
        // now convert the 2 dimensional vector into a rank3, OPstring pair list for processing by the S-system
        for(size_t i = 0; i < num_fields; i++){
          for(size_t j = 0; j < num_fields; j++){
            opdata.expressions.push_back( pair<rank3, token_list>(rank3(opdata.a_mode, i, j), opstrings[i][j]) ); 
          }} 
        //dumpOPstring(opdata); 
        return true;
      };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processblockline(model_class & mo, const vector<token> & tokens, const int spos, const int epos){
        int pos = spos;
        psystem & pmodule = mo.getPsystem();
        const string & keyword = tokens[spos].word;
        if(epos - pos < 3){
          line_error("Expected '%s' declaration must have form '%s name = expr;'\n", keyword.c_str(), keyword.c_str());
        }
        pos++;
        const string & name = tokens[pos].word; pos++;
        if(tokens[pos].word != "=" ){ 
          line_error("Expected '=' following '%s' at line %i.\n", tokens[pos-1].word.c_str(), tokens[pos].line_number);
        }; pos++;
        // enforce conditions on fieldname
        if(!isVariableLegal(name.c_str())){
          line_error("Identifier '%s' contains illegal characters. Please modify.\n",name.c_str());
        }
        if(
            pmodule.getfieldbyname(name) >= 0 || pmodule.getsfieldbyname(name) >=0 || pmodule.getfuncbyname(name) >= 0 ||
            pmodule.doesvariableexist(name)
          ){
          line_error("Identifier '%s' has already been defined.\n", name.c_str());
        }
        token_list expr; // bool foundend = false; 
        preprocessor::combinetokens(tokens, expr, pos, epos);

        ParserX * p = new ParserX( pckNONE );// double * var_storage;
        mo.assignparserdata(*p, "f", false);
        mo.assigndiagfunctions(*p);
        assert( p->GetExpr().size() == 0 );
        //p->SetExpr("");
        if(istokendefinedinparser(*p, name)){
          fprintf(stderr, "Warning: Identifier '%s' has already been defined in a different context.\n", name.c_str());
          displaywarning();
        }

        Value * nv = new Value;
        p->SetExpr(expr);
        try{
          *nv = p->Eval();
        }catch(const ParserError & e){
          line_error("%s\n", e.what());
        }
        if(keyword == "field"){ 
          pmodule.mean_fields.push_back(nv);
          pmodule.mean_field_names.push_back(name);
          pmodule.mean_field_derivatives.push_back(NULL);
          pmodule.mean_field_initializers.push_back(p);
        }else if(keyword == "function"){
          pmodule.functions.push_back(p);
          pmodule.function_names.push_back(name);
          pmodule.function_values.push_back(nv);
        }else if(keyword == "variable"){
          pmodule.variables.push_back(nv);
          pmodule.variable_names.push_back(name);
          delete p;
        }
        return true;
      }
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processfluxfieldline(model_class & mo, const vector<token> & tokens, const int spos, const int epos){
        const int num_fields = mo.get_global_param_by_name(gdb_str_NUMFIELDS).getiValue();
        psystem & pmodule = mo.getPsystem();
        int pos = spos;
        if(epos - pos < 7){
          line_error("Expected field declaration must have form 'fieldname = { index, index };'\n");
        }
        pos++;
        const string & fieldname = tokens[pos].word; pos++;
        // enforce conditions on fieldname
        if(!isVariableLegal(fieldname.c_str())){
          line_error("Identifier '%s' contains illegal characters. Please modify.\n",fieldname.c_str());
        }
        if(pmodule.getfieldbyname(fieldname) >= 0 || pmodule.getsfieldbyname(fieldname) >=0 || pmodule.getfuncbyname(fieldname) >= 0){
          line_error("Identifier '%s' has already been defined in this context.\n", fieldname.c_str());
        }
        vector<token_list> args;
        // const string & opener = tokens[spos];
        if(interpretline(tokens,  spos, epos, pos, args, "{", "}") < 0){
          return -5;
        }
        if(args.size() != 2){
          line_error("Error: 'fluxfield' definitions take exactly two field index arguments.\n");
        }
        ParserX p(pckNONE);// double * var_storage;
        mo.assignparserdata(p, "f", false);
        if(istokendefinedinparser(p, fieldname)){
          fprintf(stderr, "Warning: Identifier '%s' has already been defined in a different context.\n", fieldname.c_str());
          displaywarning();
        }
        Value indices[2] = { Value(static_cast<int_type>(0)), Value(static_cast<int_type>(0)) };
        for(size_t xx = 0; xx < args.size(); xx++){
          try{
            p.SetExpr(args[xx]);
            const IValue & val = p.Eval();
            if( ! val.IsInteger() ){
              fprintf(stderr, "Error: 'fluxfield' index argument %i evaluates to a noninteger.\n", static_cast<int>(xx));
              line_error("Error in '%i' element of expression beginning on line %i.\n", static_cast<int>(xx)+1, 
                  tokens[spos].line_number);
            }
            int nv = val.GetInteger();
            if(nv < 1 || nv > num_fields){
              fprintf(stderr, "Error: 'fluxfield' arguments are field indices and must evaluate to between 1 and %i.\n",
                  num_fields);
              fprintf(stderr, "Error: Argument specified evaluated to '%i'.\n", (int)nv);
              line_error("Error in '%i' element of expression beginning on line %i.\n", static_cast<int>(xx)+1, 
                  tokens[spos].line_number);
            }
            indices[xx] = nv;
          }catch(const ParserError & e){
            line_error("Error: %s\n", e.what());
          }
        }
        pmodule.addfluxfield(fieldname, static_cast<INDEX>(indices[0].GetInteger()), 
            static_cast<INDEX>(indices[1].GetInteger()), num_fields);
        return true;
      }
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processdissipopline(model_class & mo, const vector<token> & tokens, const int spos, const int epos, spreprocdata & spredata){
        int pos = spos;
        pos ++; // march past the keyword
        if( epos - pos < 2 ){
          line_error("Expected definition of dissipation operator with form 'opname = { ..... };\n");
        }
        const string & opname = tokens[pos].word; pos ++; 
        if(!isVariableLegal(opname.c_str())){
          line_error("Identifier '%s' contains illegal characters. Please modify.\n",opname.c_str());
        }
        // the rest is an array of expressions ...
        bool multifield = false; // if there are no braces then the expression applies to all fields
        if( pos +1 <= epos ){
          multifield = ( tokens[pos+1].word != string("{") );
        }
        vector<token_list> values;
        int prpline = interpretline( tokens, pos, epos, pos, values, string("{"), string("}") );
        if( prpline < 0 ) return -3;
        const int num_fields = mo.get_global_param_by_name(gdb_str_NUMFIELDS).getiValue();
        if( static_cast<int>(values.size()) > num_fields ){
          fprintf(stderr, "Definition of dissipation operator expects only '%i' elements.\n", num_fields);
          line_error("The number of elements must be less than or equal to the number of fields.\n");
        }
        Value k(static_cast<float_type>(1));
        Value vari(static_cast<int_type>(1));
        ParserX p(pckNONE);
        mo.assignparserdata(p, "if", false);
        mo.assigndiagfunctions(p);
        forceDefineVar(p, "k", &k);
        forceDefineVar(p, "i", &vari);
        // test all the expressions
        for(size_t iter = 0; iter < values.size(); iter++){
          p.SetExpr(values[iter]);
          try{ p.Eval(); }
          catch(const ParserError & e){
            line_error("%s\nError in element '%i' on right hand side of statement.", e.what(), static_cast<int>(iter));
          }
        }
        if( multifield ) {
          for(INDEX i = 0; i < static_cast<INDEX>(num_fields); i++){
            spredata.dissip_data.push_back( pair< INDEX, token_list >( i, values.back() ) );
          }
        }else{ 
          for(size_t iter = 0; iter < values.size(); iter++){
            spredata.dissip_data.push_back( pair<INDEX, token_list >( static_cast<INDEX>(iter), values[iter] ) );
          }
        }
        return true; 
      };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processinhomogeneousopline(model_class & mo, const vector<token> & tokens, const int spos, const int epos, spreprocdata & spredata)
      {
        int pos = spos;
        if(epos - pos < 11){
          line_error("Initial data definition must have form inhomo_op field[indices,,](k) = expr;\n");
        } pos++; // marches past initial token
        const string & name = tokens[pos].word; pos++;
        if(!isVariableLegal(name.c_str())){
          line_error("Identifier '%s' contains illegal characters. Please modify.\n",name.c_str());
        }
        // create a new rule
        //initialdata::rule newrule;
        // now next argument can be a [ to specify field indices
        vector<token_list> str_indices, str_ang_modes, str_kargs;
        while( tokens[pos].word != "=" && pos <= epos){
          if(tokens[pos].word == "["){
            if(interpretarray1d(tokens, pos, pos, str_indices, "[", "]") < 0) return false;
          };
          if(tokens[pos].word == "<"){
            if(interpretarray1d(tokens, pos, pos, str_ang_modes, "<", ">") < 0) return false;
          };
          if(tokens[pos].word == "("){
            if(interpretarray1d(tokens, pos, pos, str_kargs, "(", ")") < 0) return false;
          }
        }
        // couple of length checks
        if( str_indices.size() > 2 ){
          line_error("Error: Only two field indices are allowed.\n");
        }
        if( str_ang_modes.size() > 1){
          line_error("Error: Only one angular mode index is allowed.\n");
        }
        if( str_kargs.size() > 1){
          line_error("Error: Only one k-space wavenumber variable is allowed.\n");
        }
        if(tokens[pos].word != "="){
          line_error("Error: Expected '=' in assignment statement on line %i.\n", tokens[pos].line_number);
        }
        pos++;
        token_list expr;
        preprocessor::combinetokens(tokens, expr, pos, epos);
        if(expr.size() <= 0){
          line_error("Error: Expected statement after '=' on line %i.\n", tokens[epos].line_number);
        }
        // now we go through and define necessary variables if applicable and check parser expressions
        ParserX P(pckNONE); // testing parser
        mo.assignparserdata(P, "ifc", false);
        mo.assigndiagfunctions(P);

        spredata.inhomogeneous_data.push_back(spreprocdata::ihomo_data());
        spreprocdata::ihomo_data & idata = spredata.inhomogeneous_data.back();

        idata.name = name;

        const int num_fields = mo.get_global_param_by_name(gdb_str_NUMFIELDS).getiValue();
        const vector<int> & nkdomain = mo.get_global_prof_by_name(gdb_prof_str_NK_DOMAIN).getiData();

        if ( str_kargs.size() > 0 ){ // the user has designated a symbol other than "k" to represent
          // the explicit wavenumber variable. There is no control by the user over which values it
          // can potentially take.
          if(isVariableLegal( convert_expr_to_string(str_kargs[0]).c_str()) ){ // possibly we need to define a variable here
            rassert( str_kargs[0].size() == 0 );
            idata.k_var_name = str_kargs[0].back(); 
          }else{ // we have too many arguments and/or a bad variable name
            line_error("Bad symbol provided as k-space wavenumber argument.\n");
          }
        }

        rank3 & ipos = idata.pos;
        INDEX & ang_mode = ipos[0];
        INDEX & field_i  = ipos[1];
        INDEX & field_j  = ipos[2];
        ang_mode = -1;
        field_i = -1;
        field_j = -1; 

        // try and evaluate the contents between < > brackets. That is either a variable token or an expression indicating which mode
        // we are defining. 
        if( str_ang_modes.size() > 0 ){
          const token_list & str_ang_mode = str_ang_modes[0]; 
          P.SetExpr(str_ang_mode);
          try{
            const IValue & val = P.Eval();
            if( ! val.IsInteger() ){
              line_error("Angular mode expression must evaluate to an integer.\n");
            }
            int ival = val.GetInteger();
            if( ival < 0 || ival > nkdomain.back() ){
              line_error("Angular mode expression evaluates out of range.\n");
            }
            if( isVariableLegal( convert_expr_to_string(str_ang_mode).c_str()) ){
              fprintf(stderr, "Warning: Token '%s' has a predefined meaning and evaluates to a constant value.\n", 
                  convert_expr_to_string(str_ang_mode).c_str());
              fprintf(stderr, "         This token will not be treated as a variable.\n");
              fprintf(stderr, "Warning in angular mode definition in statement beginning on line '%i' in file '%s'.\n", 
                  tokens[spos].line_number, mo.getnamelistname(tokens[spos].namelist_number).c_str());
            }
            ang_mode = static_cast<INDEX>( ival );
          }catch(const ParserError & e){
            // if this is a single token expression with a legal variable name then this defines
            // a variable that corresponds to the angular mode
            if( isVariableLegal( convert_expr_to_string(str_ang_mode).c_str()) ){ // possibly we need to define a variable here
              // this represents a variable name, define it (this is a free variable that will iterate over all angular modes)
              rassert( str_ang_mode.size() == 1 );
              idata.ang_mode_var_name = str_ang_mode.back();	
            }else{
              line_error("%s\nError in angular mode expression.", e.what());
            }
          }
        }
        // the field index sets 
        if( str_indices.size() > 0 ) { 
          // logically the first element is the ith and the second the jth. Tokens can either be evaluted expressions
          // equal to a constant value or single token variable which can iterate over all possible values. The context
          // is clear except when a token, say "i", has been predefined, in which case it is evaluated to a single value
          // but the user may think it is a token symbole. This should be display a warning.
          for(int iter = 0; iter < static_cast<int>(str_indices.size()); iter++){
            INDEX & field = iter == 0 ? field_i : field_j ;
            const token_list & ustring = str_indices[iter]; 
            P.SetExpr(ustring);
            try{
              const IValue & val = P.Eval();
              // evaluation worked, but if it is single token and a legal variable name print out a warning
              if( isVariableLegal( convert_expr_to_string(ustring).c_str())){
                fprintf(stderr, "Warning: Single token expression evaluates to value '%s'.\n", cast_to_string(val).c_str());
                fprintf(stderr, "         Symbol will not be treated as user defined variable.\n");
                fprintf(stderr, "Warning in field index '%i' definition in expression beginning on line '%i' in file '%s'.\n",
                    iter+1, tokens[spos].line_number, mo.getnamelistname(tokens[spos].namelist_number).c_str());
              }
              if( ! val.IsInteger() ){
                line_error("Field index expression %i must evaluate to an integer.\n", iter+1);
              }
              int ival = val.GetInteger();
              if( ival < 1 || ival > num_fields ){
                line_error("Field index expression %i evaluates out of range.\n", iter+1);
              }
              field = static_cast<INDEX>( ival );
            }catch(const ParserError & e){
              // if this is a single token expression with a legal variable name then this defines
              // a variable that corresponds to the angular mode
              if( isVariableLegal( convert_expr_to_string(ustring).c_str()) ){ // possibly we need to define a variable here
                // this represents a variable name, define it (this is a free variable that will iterate over all angular modes)
                rassert( ustring.size() == 1);
                if(iter == 0){
                  idata.field_i_var_name = ustring.back(); 
                }else{
                  idata.field_j_var_name = ustring.back();
                }
              }else{
                line_error("%s\nError in angular mode expression.", e.what());
              }
            }

          }
        }
        // now we need to take the expression to the right of the equal sign, assign it to the parser
        // evaluate it and assign it to the idata source for later processing.
        P.SetExpr(expr);
        Value dummy;
        if( idata.ang_mode_var_name.size() > 0 ) forceDefineVar(P, idata.ang_mode_var_name.c_str(), & dummy );
        if( idata.k_var_name.size() > 0 ) forceDefineVar(P, idata.k_var_name.c_str(), & dummy );
        if( idata.field_i_var_name.size() > 0 ) forceDefineVar(P, idata.field_i_var_name.c_str(), & dummy );
        if( idata.field_j_var_name.size() > 0 ) forceDefineVar(P, idata.field_j_var_name.c_str(), & dummy );
        try {
          P.Eval();
          // force define all variables that have token definitions
        } catch ( const ParserError & e ){
          line_error("%s\nError evaluating the right-hand side of the expression.", e.what());
        } 
        idata.expr = expr;
        return true; 
      };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processfuncline(model_class & mo, const vector<token> & tokens, const  int spos, const int epos)
      {
        psystem & pmodule = mo.getPsystem();
        int pos = spos;
        if(epos - pos < 3){ 
          line_error("Expected function declaration must have form 'function funcname = expr;'\n"); 
        };
        pos++;
        const string & funcname = tokens[pos].word; pos++;
        if(tokens[pos].word != "="){ 
          line_error("Expected '=' following '%s' at line %i.\n", tokens[pos-1].word.c_str(), tokens[pos].line_number);
        }; pos++;
        // enforce conditions on funcname
        if(!isVariableLegal(funcname.c_str())){
          line_error("Identifier '%s' contains illegal characters. Please modify.\n", funcname.c_str());
        }
        if(pmodule.getfieldbyname(funcname) >= 0 || pmodule.getsfieldbyname(funcname) >= 0 || pmodule.getfuncbyname(funcname) >= 0){
          line_error("Identifier '%s' has already been defined.\n", funcname.c_str());
        }
        // now load the expression . . . . . .
        token_list expr; 
        for( ; pos <= epos; pos++){
          expr.push_back( tokens[pos].word ); // += tokens[pos].word;
        }
        ParserX * func = new ParserX;
        mo.assignparserdata(*(func), "f", true); // use optimization for P-system token evaluations
        if(istokendefinedinparser(*func, funcname)){
          fprintf(stderr, "Warning: Identifier '%s' has already been defined in a different context.\n", funcname.c_str());
          displaywarning();
        }
        func->SetExpr(expr);
        pmodule.functions.push_back(func);
        pmodule.function_names.push_back(funcname);
        Value * fval = new Value;
        pmodule.function_values.push_back(fval);
        try{
          *fval = func->Eval();
        }catch(const ParserError & e){
          line_error("Error: %s\n", e.what());
        }
        return true;
      };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processderivline(model_class & mo, const vector<token> & tokens, const int spos, const int epos){
        // form is derivative (field) name = expr ;
        psystem & pmodule = mo.getPsystem();
        int pos = spos;
        if(epos - pos < 6){
          line_error("'derivative' function declaration must have form 'derivative (field) funcname = expr;'\n");
        } 
        pos++; // expect (
        if(tokens[pos].word != "(" || tokens[pos+2].word != ")"){
          line_error("'derivative' function declaration must have form 'derivative (field) funcname = expr;'\n");
        }; pos ++;
        const string & fieldname = tokens[pos].word; pos+=2;
        const string & funcname = tokens[pos].word; pos++;
        if(tokens[pos].word != "="){
          line_error("'derivative' function declaration must have form 'derivative (field) funcname = expr;'\n");
        }; 
        pos++;
        // make sure the func name meets standards
        if(!isVariableLegal(funcname.c_str())){
          line_error("Identifier '%s' contains illegal characters. Please modify.\n", funcname.c_str());
        }
        if(pmodule.getfieldbyname(funcname) >= 0 || pmodule.getsfieldbyname(funcname) >= 0 || pmodule.getfuncbyname(funcname) >= 0){
          line_error("Identifier '%s' has already been defined.\n", funcname.c_str());
        }
        // check to see that field exists
        int field_index = pmodule.getfieldbyname(fieldname);
        if(field_index < 0){
          line_error("Field '%s' has not been declared. Cannot define its derivative.\n", fieldname.c_str());
        }
        token_list expr;
        // now complete the expression
        for( ; pos <= epos; pos++){
          expr.push_back(tokens[pos].word); // += tokens[pos].word; 
        }
        ParserX * func = new ParserX(pckNONE);
        mo.assignparserdata(*(func), "f", true);
        Variable & deriv = pmodule.mean_field_derivatives[field_index];
        if(deriv.GetPtr() == NULL) { 
          deriv.Bind( new Value );
          // pmodule.mean_field_derivatives[field_index] = deriv;
        }
        func->SetExpr(expr);
        pmodule.functions.push_back(func);
        pmodule.function_names.push_back(funcname);
        pmodule.function_values.push_back(deriv);
        if(istokendefinedinparser(*func, funcname)){
          fprintf(stderr, "Warning: Identifier '%s' has already been defined in a different context.\n", fieldname.c_str());
          displaywarning();
        }
        try{
          deriv = func->Eval();
        }catch(const ParserError & e){
          line_error("Error: %s\n", e.what());
        }
        return true;
      };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processdatablockcontrol(model_class & mo, const vector<token> & tokens, const int spos, const int epos){
        int pos = spos;
        const string & keyword = tokens[pos].word;
        pos++;
        if( tokens[pos].word != "(" ){ // error
          line_error("Missing '(' after token '%s' on line '%i'.\n",keyword.c_str(), tokens[spos].line_number);
        }
        if( epos - pos < 3 ){
          line_error("Token '%s' must have form %s(condition, action).\n", keyword.c_str(), keyword.c_str());
        }
        vector<token_list> str_args;
        if(interpretarray1d(tokens, pos, pos, str_args, "(", ")") < 0) return false;
        if( str_args.size() != 2 ){
          line_error("Token '%s' takes 2 arguments.\n", keyword.c_str());
        }
        ParserX P(pckNONE);
        mo.assignparserdata(P, "v", false);
        P.SetExpr( str_args[0] ); 
        bool condition = false;
        try{ condition = cast_to_bool(P.Eval()); }
        catch(const ParserError & e){
          fprintf(stderr, "Error: %s\n", e.what());
          line_error("Error in conditional statement beginning on line '%i'.\n", tokens[spos].line_number);
        }
        if( condition ){
          ParserX eval(pckNONE);
          mo.assignparserdata(eval, "v", false);
          eval.SetExpr( str_args[1] );
          try{ eval.Eval(); }catch(const ParserError & e){
            fprintf(stderr, "Error: %s\n", e.what());
            line_error("Error in action statement beginning on line '%i'.\n", tokens[spos].line_number);
          }
        }
        return true;
      }
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processinitdataline(model_class & mo, const vector<token> & tokens, const int spos, const int epos, spreprocdata & spdata)
      {
        int pos = spos;
        if(epos - pos < 9) line_error( "Initial data definition must have form 'initdata fourier_angular Phi<mk> [ i, j ] (k) = expr;\n");
        pos++;
        // create a new rule
        //initialdata::rule newrule;
        spdata.initial_conditions->rules.push_back(initialdata::rule());
        initialdata::rule & idrule = spdata.initial_conditions->rules.back();

        mo.assignparserdata(idrule.parser, "ifc", false); 
        mo.assigndiagfunctions(idrule.parser);

        idrule.namelist = mo.getnamelistname(tokens[spos].namelist_number);
        idrule.line_number = tokens[pos].line_number;
        // now define rule spacetype
        if( !matchkeyword(tokens[pos].word, "angular", "fourier_angular" ))
          line_error("Error: Unknown space designation '%s' on line %i.\n", 
              tokens[pos].word.c_str(), tokens[pos].line_number);
        pos++;
        // now load rule name
        if( ! matchkeyword( tokens[pos].word, "phi", "Phi", "cov", "Cov" ) )
          line_error("Error: Unknown initial field '%s' on line %i.\n", tokens[pos].word.c_str(), tokens[pos].line_number);
        pos++;
        // now next argument can be a [ to specify field indices
        // args should be allowed in any order
        while( tokens[pos].word != "=" && pos <= epos ){
          if(tokens[pos].word == "["){
            if( interpretfieldindices(mo, tokens, pos, idrule, *(spdata.initial_conditions)) < 0) return false;
          }
          // now next argument can be a ( to specify domain index dependencies
          else if(tokens[pos].word == "("){
            if( interpretdomainarguments(mo, tokens, pos, idrule, *(spdata.initial_conditions)) < 0) return false;
          }
          // now the next argument can be a < to specify the ang mode indices
          else if(tokens[pos].word == "<"){
            if( interpretangularmodes(mo, tokens, pos, idrule, *(spdata.initial_conditions)) < 0) return -7;
          }else{
            line_error("Error: unrecognized token '%s' in initial data definition.", tokens[pos].word.c_str());
          }
        }
        if(tokens[pos].word != "="){
          line_error("Error: Expected '=' in assignment statement on line %i.\n", tokens[pos].line_number);
        } pos++;
        // the rest of the expression is a parser complex expression
        token_list expr;
        preprocessor::combinetokens(tokens, expr, pos, epos);
        if(expr.size() == 0){
          line_error("Error: expected expression to the right of the '='.");
        }else{
          idrule.parser.SetExpr(expr);
          // attempt to parse the expression
          try{
            const IValue & val = idrule.parser.Eval();
            if(! val.IsScalar() ){
              line_error("Error: expression to the right of the '=' must be a complex valued scalar.");
            }
          }catch(const ParserError & e){
            fprintf(stderr, "Error: %s", e.what());
            line_error("Error evaluating expression to the right of the '='.");
          }
        }

        return true;
      };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      static bool processlinearopline(model_class & mo, const vector<token> & tokens, const int spos, const int epos, spreprocdata & spdata, bool include_op)
      {
        psystem & pmodule = mo.getPsystem();
        // bool include_op = true --> set the opstatus so that the ssystem preprocessor does angular decomp on it,
        //                            otherwise it is just a definition to be used elsewhere.
        int pos = spos;
        const int numfields = mo.get_global_param_by_name(gdb_str_NUMFIELDS).getiValue();
        if(epos - pos < 3) line_error("Expected field declaration must have form 'linearop opname = expr;'\n");
        pos++;
        const string & opname = tokens[pos].word; 
        pos++;

        vector<token_list> str_indices, str_ang_modes;
        while( tokens[pos].word != "=" && pos <= epos){
          if(tokens[pos].word == "["){
            if(interpretarray1d(tokens, pos, pos, str_indices, "[", "]") < 0) return false;
          };
          if(tokens[pos].word == "<"){
            if(interpretarray1d(tokens, pos, pos, str_ang_modes, "<", ">") < 0) return false;
          };
        }

        INDEX ang_mode = -1; // negative for all of them, otherwise 0, 1, 2, 3 etc .... 
        if( str_ang_modes.size() != 1 ) line_error("Error in expression withini '<>'\n%s", "Linear operator takes only 1 angular mode argument.");
        const token_list & angmodeexpr = str_ang_modes[0];
        // we need a numerical value from the expression
        {
          ParserX p(pckNONE);
          mo.assignparserdata(p, "ifc", false);
          assigndiagfuncs(p, false);
          p.SetExpr(angmodeexpr);
          try{ const IValue & val = p.Eval();
            ang_mode = static_cast<INDEX>(val.GetInteger());
          }catch(const ParserError & e){
            line_error("%s\n%s", e.what(), "Error evaluating expression within '<>'.");
          }
        }
        // we want to support the definition of field labels for specific assignments no in matrix form.
        bool process_matrix_syntax = true;
        INDEX fields[2] = { -1, -1 };
        if( str_indices.size() > 0 ){
          if( str_indices.size() != 2 )
            line_error("Linear operators have rank 2 in field index space. They require 2 field index labels.\nSince an explicit field index was specified, 2 field indices are expected but %i was given.", static_cast<int>(str_indices.size()));
          ParserX p(pckNONE);
          mo.assignparserdata(p, "ifc", false);
          assigndiagfuncs(p, false);

          for(size_t iter = 0; iter < str_indices.size(); iter++){
            // must evaluate to integers
            p.SetExpr( str_indices[iter] );
            try{ const IValue & val = p.Eval();
              // must be an integer and must lie within the correct range of field indices
              if( ! does_value_conform_to_type(val, 'i') ) line_error("Expression within '[]' must evaluate to an integer.\n"); 
              process_matrix_syntax = false;
              fields[iter] = static_cast<INDEX>(val.GetInteger());
              if( fields[iter] < 1 || fields[iter] > numfields )
                line_error("Expression within '[]' must evaluate to a number between 1 and %i.\n", numfields);
            }catch(const ParserError & e){
              line_error("%s\nFailed to evaluate expression designating index for field %i\n%s", e.what(), static_cast<int>(iter),
                  "Error in expression within '[]'.")
            }
          }
        } 
        if(tokens[pos].word != "=" ){ 
          line_error("Expected '=' following '%s' at line %i.\n", tokens[pos-1].word.c_str(), tokens[pos].line_number);
        }; pos++;
        // enforce conditions on fieldname
        if(!isVariableLegal(opname.c_str())){
          line_error("Operator identifier '%s' contains illegal characters. Please modify.\n", opname.c_str());
        }
        if(pmodule.getfieldbyname(opname) >= 0 || pmodule.getsfieldbyname(opname) >=0 || pmodule.getfuncbyname(opname) >= 0){
          line_error("Identifier '%s' has already been defined.\n", opname.c_str());
        }
        // check if operator already exists with the name provided
        bool op_already_exists = false;
        int op_pos = -1;
        for(size_t iter = 0; iter < spdata.OPdata.size(); iter++){
          if( spdata.OPdata[iter].OPname == opname ){
            op_already_exists = true;
            op_pos = static_cast<int>(iter);
            break;
          }
        }
        if( ! op_already_exists ){
          linearOPtranslationdata opdata;
          spdata.OPdata.push_back(opdata);
          spdata.OPdata.back().OPname = opname;
          spdata.OPdata.back().evaluate_op = include_op;
          spdata.OPdata.back().a_mode = ang_mode;
          op_pos = static_cast<int>(spdata.OPdata.size()) - 1;
        }else{ // check for consistency
          linearOPtranslationdata & opdata = spdata.OPdata[op_pos];
          if( opdata.evaluate_op != include_op ){
            line_error("Operator '%s' was defined previously in a different operator context.\nPrevious definition specified operator as a '%s'."
                , opname.c_str(), opdata.evaluate_op ? "linear operator" :"linear component");
          }
          if( opdata.a_mode >= 0 && opdata.a_mode != ang_mode ){
            line_error("Operator '%s' was defined previously with angular mode '%i'\n\tbut was defined here with angular mode '%i'.\n",
                opname.c_str(), static_cast<int>( opdata.a_mode ), ang_mode );
          }
          // finally, print a warning if operator exists and we are defining things in matrix mode which will erase previous data
          if( process_matrix_syntax ){
            fprintf(stderr, "Warning: Operator '%s' was previously defined but will be redefined\n", opname.c_str());
            fprintf(stderr, "         by statement beginning on line '%i' in file '%s'.\n", 
                tokens[spos].line_number, mo.getnamelistname(tokens[spos].namelist_number).c_str());
            // clear the data
            opdata.expressions.clear();
          }
        }
        linearOPtranslationdata & opdata = spdata.OPdata[op_pos];
        int mspos = pos;
        int mepos = epos; 
        if( process_matrix_syntax ){
          int status = processlinearopblock(mo, tokens, mspos, mepos, spdata, op_pos);
          if(status < 0) return -7;
        }else{ 
          // we have specific positions that are to be defined 
          // process the statement which is enclosed in [] brackets
          // must have statement of the expr;
          token_list element; // complex element expression
          {
            preprocessor::combinetokens(tokens, element, static_cast<unsigned int>(pos), static_cast<unsigned int>(epos) );
          }
          // if the rank3 structure has already been defined for the current op we print a warning
          rank3 rpos( static_cast<INDEX>( ang_mode ), fields[0]-1, fields[1]-1 );
          int ipos = -1;
          for(size_t iter = 0; iter < opdata.expressions.size(); iter++){
            if( opdata.expressions[iter].first == rpos ){
              ipos = static_cast<int>(iter);
              fprintf(stderr, "Warning: m=%i, i=%i, j=%i expression was previously defined for linear operator '%s'.\n",
                  ang_mode, static_cast<int>( fields[0] ), static_cast<int>( fields[1] ), opname.c_str() );
              fprintf(stderr, "         Data will be erased and reassigned.\n");
              displaywarning();
              break; 
            }
          }
          // need to test the element expressions to make sure they parse correctly
          ParserX P(pckNONE); // used just for syntax checking
          mo.assignparserdata(P,"if", false);
          mo.assigndiagfunctions(P);
          Value  m = static_cast<int_type>(1);
          Value  k = static_cast<float_type>(1);
          Value  i = static_cast<int_type>(1);
          Value  j = static_cast<int_type>(1);

          forceDefineVar(P, "k", &k);
          forceDefineVar(P, "m", &m);
          forceDefineVar(P, "i", &i);
          forceDefineVar(P, "j", &j);

          P.SetExpr( element );
          try{ 
            P.Eval(); 
          }catch(const ParserError & e){
            line_error("Error: %s.\nError evaluating expression '%s'.\n", e.what(), convert_expr_to_string(element).c_str());
          }
          if( ipos < 0 ){
            opdata.expressions.push_back( pair<rank3, token_list>( rpos, element ) );
          }else{
            opdata.expressions[ipos].first = rpos;
            opdata.expressions[ipos].second = element;
          }
        }
        return true;
      };
      /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
      // local macro 
#define assert_format(boolean) if( !( boolean) ){ \
  fprintf(stderr, "Error: Function delcarations take the form '%s'.\n",\
      "function [return-type] name([type] arg1,[type] arg2,arg3,....,argn)"); \
  fprintf(stderr, "Error in declaration of function '%s' on line '%i'.\n", funcname.c_str(), tokens[spos].line_number); \
  return false; \
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static bool processnonlinearopline(model_class & mo, const vector<token> & tokens, const int spos, const int epos)
{
  psystem & pmodule = mo.getPsystem();
  // bool include_op = true --> set the opstatus so that the ssystem preprocessor does angular decomp on it,
  //                            otherwise it is just a definition to be used elsewhere.
  int pos = spos;
  const int numfields = mo.get_global_param_by_name(gdb_str_NUMFIELDS).getiValue();
  if(epos - pos < 4) line_error("Expected field declaration must have form 'nonlinearop opname [ i, j, ... ] (k,..) = expr;'\n");
  pos++;
  const string & opname = tokens[pos].word; 
  pos++;

  vector<token_list> str_field_indices, str_ang_modes, str_kspace_variables;

  // nonlinear operators contain a single expression (you cannot at this time specify a sparse array rule)
  // therefore, the items in the [ ... ], ( ... ), < .... > must be variables !


  ParserX testP(pckNONE); // testing parser
  mo.assignparserdata(testP, "ifc", false);
  mo.assigndiagfunctions(testP);

  while( tokens[pos].word != "=" && pos <= epos){
    if(tokens[pos].word == "["){
      if(interpretarray1d(tokens, pos, pos, str_field_indices, "[", "]") < 0) return false;
      for(int i = 0; i < str_field_indices.size(); i++){ 
        if(!isVariableLegal( convert_expr_to_string(str_field_indices[i]).c_str())) line_error("Element %i in list enclosed by '[]' is not a legal variable.\n", i+1);
        rassert( str_field_indices[i].size() == 1 );
        if( isTokenDefined( str_field_indices[i].back().c_str(), testP ) ){
          fprintf(stderr, "Warning: Variable %s in field index list shadows previously defined symbol.\n", str_field_indices[i].back().c_str());
          fprintf(stderr, "Warning on line '%i' of file '%s'.\n", tokens[pos].line_number, mo.getnamelistname(tokens[pos].namelist_number).c_str());    
        }
      }
    };
    if(tokens[pos].word == "<"){
      if(interpretarray1d(tokens, pos, pos, str_ang_modes, "<", ">") < 0) return false;
      for(int i = 0; i < str_ang_modes.size(); i++){ 
        if(!isVariableLegal(convert_expr_to_string(str_ang_modes[i]).c_str())) line_error("Element %i in list enclosed by '<>' is not a legal variable.\n", i+1);
        rassert( str_ang_modes[i].size() == 1 );
        if( isTokenDefined( str_field_indices[i].back().c_str(), testP ) ){
          fprintf(stderr, "Warning: Variable %s in angular mode list shadows previously defined symbol.\n", str_field_indices[i].back().c_str());
          fprintf(stderr, "Warning on line '%i' of file '%s'.\n", tokens[pos].line_number, mo.getnamelistname(tokens[pos].namelist_number).c_str());    
        }
      }
    };
    if(tokens[pos].word == "("){
      if(interpretarray1d(tokens, pos, pos, str_kspace_variables, "(", ")") < 0) return false;
      for(int i = 0; i < str_kspace_variables.size(); i++){ 
        if(!isVariableLegal(convert_expr_to_string(str_kspace_variables[i]).c_str())) line_error("Element %i in list enclosed by '()' is not a legal variable.\n", 
                            i+1);
        rassert( str_kspace_variables[i].size() == 1 );
        if( isTokenDefined( str_kspace_variables[i].back().c_str(), testP ) ){
          fprintf(stderr, "Warning: Variable %s in wavenumber list shadows previously defined symbol.\n", str_kspace_variables[i].back().c_str());
          fprintf(stderr, "Warning on line '%i' of file '%s'.\n", tokens[pos].line_number, mo.getnamelistname(tokens[pos].namelist_number).c_str());    
        }
      }
    }
  }
  if(pos > epos || tokens[pos].word != "="){
    line_error("Nonlinear operator definition requires '=' after '%s' on line: %i.\n", 
        tokens[pos-1].word.c_str(),tokens[pos-1].line_number);
  }
  pos++;

  // only some operator names are allows: A, M, Q, R, S, P
  if(! get_custom_func_container(opname)){
    line_error("Unrecognized nonlinear operator by the name %s.\n", opname.c_str());
  }

  // variable lists are small (so this horribly inefficient algorithm should be ok)
  // no repetition in variable list!
  {  vector<string> vlist;
    for(int i = 0; i < str_field_indices.size(); i++) vlist.push_back( str_field_indices[i].back() );
    for(int i = 0; i < str_kspace_variables.size(); i++) vlist.push_back( str_kspace_variables[i].back() );
    for(int i = 0; i < str_ang_modes.size(); i++) vlist.push_back( str_ang_modes[i].back() );
    for(int i = 0; i < vlist.size(); i++){
      for(int j = i+1; j < vlist.size(); j++){
        if(vlist[i] == vlist[j]){ // not allowed
          line_error("Token %s found twice in expression.\nNonlinear operator field, wavenumber, and angular mode variables must all be unique.\n",
              vlist[i].c_str());
        }
      }
    }
  }

  try {
    custom_func_container & cp = *get_custom_func_container(opname);
    const int num_ang_modes = cp.getnum_ang_modes();
    if( num_ang_modes != str_ang_modes.size() ) line_error("Nonlinear operator %s takes %i angular modes but %i symbols were provided.\n",
        opname.c_str(), num_ang_modes, str_ang_modes.size());
    
    vector<string> ang_mode_names( str_ang_modes.size(), string() );
    for(size_t i = 0; i < str_ang_modes.size(); i++) ang_mode_names[i] = str_ang_modes[i].back();
    cp.assign_angspace_names( ang_mode_names );

    const int num_wavenumbers = cp.getnum_kspace_variables();
    if( num_wavenumbers != str_kspace_variables.size() ) line_error("Nonliner operator %s takes %i wavenumber variables but  %i symbols were provided.\n",
        opname.c_str(), num_wavenumbers, str_kspace_variables.size());

    vector<string> kspace_variables_names( str_kspace_variables.size(), string() );
    for(size_t i = 0; i < str_kspace_variables.size(); i++) kspace_variables_names[i] = str_kspace_variables[i].back();
    cp.assignkspacenames( kspace_variables_names );

    const int num_fields = cp.getnum_fields();
    if( num_fields != str_field_indices.size()) line_error("Nonlinear operator %s takes %i field variables but  %i symbols were provided.\n",
        opname.c_str(), num_fields, str_field_indices.size());
 
    vector<string> field_indices_names( str_field_indices.size(), string() );
    for(size_t i = 0; i < field_indices_names.size(); i++) field_indices_names[i] = str_field_indices[i].back();
    cp.assign_field_names(field_indices_names);

    token_list expr;
    preprocessor::combinetokens(tokens, expr, pos, epos);
    cp.assignexpr( expr ); // evaluates automatically
  }catch(const ParserError & e){
    line_error("Error: %s.\nError in expression on the right hand side of nonlinear operator assignment.\n", e.what());
  }

  return true;
};
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// local macro 
#define assert_format(boolean) if( !( boolean) ){ \
  fprintf(stderr, "Error: Function delcarations take the form '%s'.\n",\
      "function [return-type] name([type] arg1,[type] arg2,arg3,....,argn)"); \
  fprintf(stderr, "Error in declaration of function '%s' on line '%i'.\n", funcname.c_str(), tokens[spos].line_number); \
  return false; \
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static bool processfunctiondeclare(diag_compiler & dcomp, const vector<token> & tokens, int & pos, const int epos, vector< pair<char_type,string> > & arglist)
{
  model_class & mo = *(dcomp.moptr);
  // expect the following form name(var1, var2, var3, var4)
  //make sure pos < epos , etc ...
  // a few assertions (returns an error if false);
  char_type ret_type = 'v'; // normal variable return type
  pos++;
  const int spos = pos;
  if( is_string_var_type( tokens[pos].word ) ){
    ret_type = get_var_type_from_string(tokens[pos].word);
    pos++;
  }
  const string & funcname = tokens[pos].word;
  assert_format( funcname != "(" );
  // check consistency of function name
  if( !isVariableLegal( funcname.c_str() ) ){
    fprintf(stderr, "Error: Function name '%s' is not legal.\n", funcname.c_str() );
    assert_format(false);
  }
  assert_format(pos+2 <= epos);
  assert_format(tokens[pos+1].word=="(");
  // find the matching ')'
  int cpos = -1;
  arglist.push_back( pair<char_type, string>('v', "") );
  bool type_specified = false;
  for(pos = pos+2; pos <= epos; pos++){
    if( tokens[pos].word == ")" ){
      if(tokens[pos-1].word != "("){ // check to make sure all args were defined properly
        if(arglist.size() > 0){ assert_format( arglist.back().second.size() > 0 ) };
      }else{
        arglist.clear();
      };
      cpos = pos;
      break;
    }else if( tokens[pos].word == "," ){ // expect a new argument to follow
      arglist.push_back( pair<char_type, string>('v', "") );
      type_specified = false;
    }else{ // we expect an argument name
      assert_format( arglist.size() > 0 );
      assert_format( arglist.back().second.size() == 0 );
      const string & argname = tokens[pos].word;
      if( is_string_var_type( argname ) && ! type_specified ){
        arglist.back().first = get_var_type_from_string(argname);
        type_specified = true;
      }else{
        arglist.back().second = argname;
        // check legality of variable name
        if( !isVariableLegal( argname.c_str() ) ){
          fprintf(stderr, "Variable name '%s' is illegal at position '%i' of argument list.\n", argname.c_str(), 
              static_cast<int>(arglist.size())-1);
          fprintf(stderr, "Function argument list cannot contain illegal variable names.\n");
          assert_format(false);
        }
        // make sure variable was not previously defined ....
        for(size_t iter = 0; iter < arglist.size() - 1; iter++){
          if( arglist[iter].second == argname ){ // error
            fprintf(stderr, "Function argument list cannot contain repeated variables.\n");
            assert_format(false);
          }
        }
      }
    }
  }
  // make sure that the cpos was found
  assert_format(cpos >= 0);
  pos = cpos;
  // now go through variables and make sure they do not refer to previously defined tokens in a parser
  ParserX P(pckNONE);
  mo.assignparserdata(P,"v",false);
  assigndiagfuncs(P, false);
  // double val = 0;
  for(size_t iter = 0; iter < arglist.size(); iter++){
    // try to assign the variable
    const string & varname = arglist[iter].second;
    if( isTokenDefined( varname, P ) ){
      fprintf(stderr, "Warning: Variable '%s' shadows a previously defined token.\n", varname.c_str());
      displaywarning();
    }
  }
  // add the diagnostics block etc ..
  dcomp.makefunction( arglist, funcname, ret_type );
  // define the appropriate variables for the diagnostic block based on their name
  return true;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#define diag_error(msg...) { \
  fprintf(stderr, "Error: "); \
  fprintf(stderr, msg); \
  fprintf(stderr, "\nDIAGNOSTIC: Error in statement beginning on line '%i'.\n", tokens[spos].line_number); \
  return false; \
};

#define diag_error_if(cond,errmsg...) { if(cond){ diag_error(errmsg); }; };
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static bool processdiagnosticdeclare(diag_compiler & dcompiler, int & last_instr, const vector<token> & tokens, int & pos, int spos, int epos){
  // a line is now passed to this function, which means there will be no semicolon!
  model_class & mo = *(dcompiler.moptr);
  model_diagnostics_block * block = dcompiler.diagblock;
  bool declare_static = false; // 
  spos = pos;
  vector<string> varnames( 1, "");
  pos++;
  if(pos > epos){
    line_error("Error: DECLARE must name a variable name after statement.\n");
  }
  // check for special qualifiers
  if(tokens[pos].word == "STATIC"){
    declare_static = true;
    pos++;
  }else if(tokens[pos].word == "DYNAMIC"){
    declare_static = false;
    pos++;
  }
  char_type var_type = 'v'; // variables by default have no fixed type
  if(is_string_var_type(tokens[pos].word)){
    var_type = get_var_type_from_string(tokens[pos].word);
    pos++;
  }
  // check for type identifiers
  if(pos > epos){
    line_error("Error: DECLARE must name a variable name after qualfier statements.\n");
  }
  while(pos <= epos && tokens[pos].word != "="){
    if(varnames.back().size() == 0){ // we expect a varname
      varnames.back() = tokens[pos].word;
      if(!isVariableLegal(varnames.back().c_str())){
        line_error("Error: Illegal variable name '%s' specified in expression starting on line '%i'.\n", 
            varnames.back().c_str(), tokens[pos].line_number);
      }
    }else if(tokens[pos].word == ","){// we expect a new variable to be declared
      varnames.push_back("");
    }else{
      line_error("Error: Unexpected '%s' starting on line '%i'.\n", tokens[pos].word.c_str(), tokens[pos].line_number);
    }
    pos++;
  }
  // if there is an assignment operator '=' we expect a statement
  Value fval(get_default_value_of_type(var_type));
  token_list rexpr;
  ParserX P(pckNONE);
  if(pos <= epos && tokens[pos].word == "="){
    int opos = pos;
    pos++;
    preprocessor::combinetokens(tokens, rexpr, pos, epos);
    if(rexpr.size() < 0){
      line_error("Error: Expected expression after '=' on line '%i'.\n", tokens[opos].line_number);
    }  
    // use Parser to evaluate the expression if
    /* P.SetExpr(rexpr);
       try{
       dcompiler.assigndiaginfo(P);
       }catch(const ParserError & e){
       line_error("Error: %s.\n", e.what());
       }
       P.SetExpr(rexpr);
       try{
       P.Eval();
       }catch(const ParserError & e){
       fprintf(stderr,"Error: %s.\n", e.what());
       line_error("Error in assignment statement starting on line '%i'.\n", tokens[spos].line_number);
       }*/
  }
  // now add the variables that were declared
  for(size_t i = 0; i < varnames.size(); i++){ // make sure variable does not yet exist
    // print a warning if the parser token was predefined before
    if(dcompiler.isvariabledefined(varnames[i])){
      fprintf(stderr, "Error: Variable '%s' was already previously declared in block.\n", varnames[i].c_str());
      line_error("Error in statement starting on line '%i'.\n", tokens[spos].line_number);
    }
    if(isTokenDefined(varnames[i], P)){
      fprintf(stderr, "Warning: Variable '%s' shadows a previously defined token.\n", varnames[i].c_str());
      displaywarning();
    }
    try{ 
      dcompiler.addvariable(varnames[i].c_str(), fval, var_type, declare_static);
    }catch(const ParserError & e){
      line_error("Error: %s\n", e.what());
    }
    // now add instructions, to initialize these variables each time the block is run
    if(rexpr.size()>0){ // if declare_static then instruction is run only 1 time.
      token_list assignExpr;
      assignExpr.push_back( varnames[i] );
      assignExpr.push_back(string("="));
      for(size_t k = 0; k < rexpr.size(); k++) assignExpr.push_back( rexpr[k] );
      //last_instr = dcompiler.addinstruction(varnames[i] + string("=") + rexpr, false, false, declare_static, false);
      last_instr = dcompiler.addinstruction( assignExpr, false, false, declare_static, false);
      if( last_instr < 0 ){
        line_error("Error in assignment statement starting on line '%i'.\n", tokens[spos].line_number);
      }
      dcompiler.addinstructiondebuginfo(tokens[spos].namelist_number, tokens[spos].line_number,
          tokens[epos].line_number);
    }
  }
  return true;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* implementing structure to carry the large number of arguments required for processing 
   a line of script. Since the functions that does this processing are broken down into many
   subcomponents, we simply employ this structure and pass it by reference. */
struct script_info {
  diag_compiler * dcompiler_ptr;
  const string * ptr_keyword;
  const vector<token> * ptr_tokens;
  int pos;
  int epos;
  int last_instr;
  stack<blocktype> blocks;
  stack< vector<int> > addresses;
  stack< triad > return_info;
  int  returns_called;
  bool cancelimplicit;
  bool addbreakpoint;
  bool runonce; 
  token_list expr; 
#define localize_script_info(obj) model_class * mo = obj.dcompiler_ptr->moptr; \
  diag_compiler & dcompiler = *(obj.dcompiler_ptr); \
  model_diagnostics_block & diagblock = *(dcompiler.diagblock); \
  const vector<token> & tokens = *( obj.ptr_tokens ); \
  const int spos = obj.pos;\
  const int epos = obj.epos;\
  int & pos = obj.pos;\
  int & last_instr = obj.last_instr;\
  stack<blocktype> & blocks = obj.blocks;\
  stack< vector<int> > & addresses = obj.addresses;\
  stack< triad > & return_info = obj.return_info;\
  int & returns_called = obj.returns_called;\
  bool & cancelimplicit = obj.cancelimplicit;\
  bool & addbreakpoint = obj.addbreakpoint;\
  bool & runonce = obj.runonce;
  private:
  // list of diag Token processing functions
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procIMPLICIT(bool flag){
    localize_script_info( (*this) );
    diagblock.setallowimplicit(flag);
    return true;
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procRUNONCE(bool flag){ 
    localize_script_info( (*this) );
    const string & keyword = * (ptr_keyword);
    if(!flag){ // turn off runonce
      if(blocks.size() > 0 && blocks.top() == runonceblock){
        blocks.pop();
        runonce = false;
      }else{
        diag_error("ENDRUNONCE encountered without matching RUNONCE.");
      }
    }else{ // turn on runoncce
      diag_error_if(runonce, "RUNONCE cannot be nested.");
      runonce = true;
      blocks.push(runonceblock);
      if( diagblock.isblockendscript() ){
        fprintf(stderr, "Warning: '%s' block may have unintended consequences within an ENDSCRIPT.\n", keyword.c_str());
        fprintf(stderr, "         Block will run only once even if multiple SIGPROF signals are sent to process.\n");
        fprintf(stderr, "Warning on line '%i' of file '%s'.\n", tokens[pos].line_number, 
            mo->getnamelistname(tokens[pos].namelist_number).c_str());
      }else if( diagblock.isblockstartscript() ){
        fprintf(stderr, "Warning: '%s' qualifier is redundant in a STARTSCRIPT.\n", keyword.c_str());  
        fprintf(stderr, "Warning on line '%i' of file '%s'.\n", tokens[pos].line_number, 
            mo->getnamelistname(tokens[pos].namelist_number).c_str());
      }
    }
    return true;
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procALLOWIMPLICIT() { localize_script_info( (*this) );
    diagblock.setallowimplicit(true);
    return true;
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procIMPLICIT() { localize_script_info( (*this) );
    this->procALLOWIMPLICIT();
    this->cancelimplicit = true;
    return true;
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procRETURN() { localize_script_info( (*this) );
    const string & keyword = * (ptr_keyword);
    diag_error_if( ! diagblock.isblockfunction(), "Cannot use %s keyword outside of function.", keyword.c_str());
    pos++; // march past directive
    preprocessor::combinetokens(tokens, expr, pos,  epos);
    if(expr.size() > 0){
      last_instr = dcompiler.addinstruction(expr, false, false, false, true);
      dcompiler.addinstructiondebuginfo(tokens[spos].namelist_number, tokens[spos].line_number,
          tokens[epos].line_number);
      if( last_instr < 0 ){ 
        diag_error_if(last_instr < 0, "Error in file %s starting on line %i.", 
            mo->getnamelistname(tokens[pos].namelist_number).c_str(),
            tokens[pos].line_number
            ); 
        return false;
      }
      // diag_error_if(last_instr < 0, "Error in statement."); (Does not provide any useful information)
    }else{
      diag_error("%s statement must be followed with an expression.", keyword.c_str());
    }
    if( return_info.top()[1] > return_info.top()[2] ){ // only count 1 return per block
      return_info.top()[2] = return_info.top()[2] + 1;
    }
    diag_error_if( return_info.top()[2] != return_info.top()[1] , "Note all control paths in block started on line '%i' return a value.", return_info.top()[0] );
    returns_called ++ ;
    pos = epos;
    return true;
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procDECLARE() { localize_script_info( (*this) );
    diag_error_if( runonce, "Cannot combine the RUNONCE directive with a DECLARE statement.");
    if(! processdiagnosticdeclare( dcompiler, last_instr, tokens, pos, spos+1, epos)){
      return false;
    }
    pos=epos;
    return true; // statement is self containing, semicolon terminated
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procENDIF() { localize_script_info( (*this) );
    const string & keyword = * (ptr_keyword);
    if(addresses.size() > 0 && blocks.top() == ifblock){
      vector<int> & ifaddresses = addresses.top();
      for(size_t i = 0; i < ifaddresses.size(); i++){
        int instr = ifaddresses[i];
        if(dcompiler.getinstructionjmpaddress(instr) < 0){
          dcompiler.assignjmpaddress(instr, last_instr+1);
        }
      }
      addresses.pop();
      blocks.pop();
      dcompiler.pop_scope();
    }else{
      diag_error("%s encountered without If", keyword.c_str());
    }
    diag_error_if( return_info.top()[2] > 0 && return_info.top()[2] < return_info.top()[1] ,
        "Note all control paths in block started on line '%i' return a value.", 
        return_info.top()[0] );
    return_info.pop();
    return true; 
  } 
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procELSE() { localize_script_info( (*this) );
    const string & keyword = * (ptr_keyword);
    /*   if(expr.size() > 0){
         diag_error("Statement not terminated by ';'.");
         }*/
    if(addresses.size() <= 0 || blocks.top() != ifblock){
      diag_error("Unexpected '%s'.", keyword.c_str());
    }
    dcompiler.pop_scope(); 
    last_instr = dcompiler.addjmpinstruction(runonce);  // add an instruction to jmp to endif
    int last_if_instruction = addresses.top().back();
    dcompiler.assignjmpaddress(last_if_instruction, last_instr+1); // jmp beyond last jmp instruction 
    addresses.top().push_back(last_instr);
    dcompiler.push_scope();
    return_info.top()[1]++; 
    return true;
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procELSEIF() { localize_script_info( (*this) );
    const string & keyword = * (ptr_keyword);
    pos++; // march beyond elseif
    if(addresses.size() <= 0 || blocks.top() != ifblock){
      diag_error("Unexpected '%s'.", keyword.c_str());
    }
    dcompiler.pop_scope(); 
    // get the rest of the instruction
    int epos2 = epos;
    if(tokens[epos].word == "then"){ // allow for statement to be terminated with a then keyword
      epos2 = epos - 1;
    }
    preprocessor::combinetokens(tokens, expr, pos, epos2); 
    int last_if_instruction = addresses.top().back(); 
    // last instruction pushed onto the vector was that of the jmpnif for either an if or elseif
    // so we store its position to update the jump address a bit later
    // before adding a jmpnif instruction, add a jmp instruction to the end of the block
    int jmp_to_end_block = dcompiler.addjmpinstruction(runonce);
    addresses.top().push_back(jmp_to_end_block); // push onto the vector on top of the stack
    // now add the jmpnif instruction
    last_instr = dcompiler.addinstruction(expr, true, false, runonce, false);
    dcompiler.addinstructiondebuginfo(tokens[spos].namelist_number,tokens[spos].line_number, tokens[epos2].line_number);
    diag_error_if(last_instr < 0, "Error in %s statement.", keyword.c_str());
    dcompiler.assignjmpaddress(last_if_instruction, last_instr); 
    // assign jmp to current instruction for the last if or elseif
    addresses.top().push_back(last_instr); 
    dcompiler.push_scope(); 
    // push address of this elseif block to be updated by either an elseif, else, or endif
    return_info.top()[1]++; 
    pos = epos;
    return true; // self containing statement
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procBREAKIF() { localize_script_info( (*this) );
    const string & keyword = * (ptr_keyword);
    pos++; // march past breakif statement
    diag_error_if(blocks.size() <= 0 || ( blocks.top() == notype ),
        "Unexpected '%s' outside of 'loop', 'if', or 'for'.", keyword.c_str());
    preprocessor::combinetokens(tokens, expr, pos, epos);
    last_instr = dcompiler.addinstruction(expr, false, true, runonce, false); // jmp address will be updated by endloop or endif statement
    dcompiler.addinstructiondebuginfo(tokens[spos].namelist_number,tokens[spos].line_number, tokens[epos].line_number);
    addresses.top().push_back(last_instr);
    diag_error_if(last_instr<0, "Error in '%s' statement.", keyword.c_str());
    return_info.top()[1]++; 
    pos = epos ;
    return true; // self containing statement 
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procFOR() { localize_script_info( (*this) );
    // just use the loop endloop constructs to develop this ...
    pos++;
    blocks.push(forblock);
    dcompiler.push_scope(); 
    addresses.push( vector<int>() );
    preprocessor::combinetokens(tokens,expr, pos, epos);
    last_instr = dcompiler.addinstruction(expr, false, false, runonce, false); // initializer
    dcompiler.addinstructiondebuginfo(tokens[spos].namelist_number,tokens[spos].line_number, 
        tokens[epos].line_number);
    diag_error_if(last_instr < 0, "Error in initializer statement of 'for' loop.");
    return_info.push( triad( tokens[spos].line_number, 1, 0 ) ); 
    pos = epos;
    return true; // self containing statement
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procDO() { localize_script_info( (*this) );
    const string & keyword = * (ptr_keyword);
    diag_error_if( blocks.size() == 0 || blocks.top() != forblock , "Unexpected '%s'.", keyword.c_str());
    last_instr = dcompiler.addjmpinstruction(runonce);
    dcompiler.assignjmpaddress( last_instr, addresses.top() [0] );
    const int jmp_beyond_incr_instr = addresses.top() [ 1 ];
    diag_error_if( dcompiler.getinstructionjmpaddress( jmp_beyond_incr_instr ) >= 0,
        "'%s' keyword already specified for the current 'for' block.", keyword.c_str());
    dcompiler.assignjmpaddress( jmp_beyond_incr_instr, last_instr+1 );
    return true; 
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procDONE() { localize_script_info( (*this) );
    const string & keyword = * (ptr_keyword);
    diag_error_if(
        blocks.size() == 0 || blocks.top() != forblock, "'%s' encountered without matching 'for'.", keyword.c_str());
    dcompiler.pop_scope(); 
    // add jmp instruction to the first address on the vector
    last_instr = dcompiler.addjmpinstruction(runonce);
    vector<int> & loopaddresses = addresses.top();
    dcompiler.assignjmpaddress(last_instr, loopaddresses[2] ); 
    // jmp back to the first incrementor instruction
    dcompiler.assignjmpaddress(loopaddresses[0], last_instr+1); // break out of loop if ! conditional 
    for(size_t i = 3; i < loopaddresses.size(); i++){ 
      // the first 3 instructions are special to the for loop and are not breakifs, etc..
      int instr = loopaddresses[i];
      if( dcompiler.getinstructionjmpaddress(instr) < 0){
        dcompiler.assignjmpaddress(instr, last_instr+1);
      } 
    } 
    blocks.pop();
    addresses.pop(); 
    diag_error_if( return_info.top()[2] > 0 && return_info.top()[2] < return_info.top()[1] ,
        "Note all control paths in block started on line '%i' return a value.", 
        return_info.top()[0] );
    return_info.pop();
    return true;
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procLOOP() { localize_script_info( (*this) );
    const string & keyword = * (ptr_keyword);
    blocks.push(loopblock);
    dcompiler.push_scope(); 
    addresses.push( vector<int>() );
    addresses.top().push_back(last_instr+1); // first element is the start of the loop location
    // all there is to it!
    return_info.push( triad( tokens[spos].line_number, 1, 0 ) ); 
    return true;
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procENDLOOP() { localize_script_info( (*this) );
    const string & keyword = * (ptr_keyword);
    diag_error_if(blocks.top() != loopblock, "'%s' encountered without matching 'loop'.", keyword.c_str());
    dcompiler.pop_scope(); 
    // add jmp instruction to the first address on the vector
    last_instr = dcompiler.addjmpinstruction(runonce);
    vector<int> & loopaddresses = addresses.top();
    dcompiler.assignjmpaddress(last_instr, loopaddresses[0] ); 
    // jmp back to instruction right after Loop token
    for(size_t i = 1; i < loopaddresses.size(); i++){ 
      // do not do the first instruction because it is the one right after Loop token and is not a breakif!
      // assign jmp addresses of ifbreak blocks to right after the last instruction
      int instr = loopaddresses[i];
      if( dcompiler.getinstructionjmpaddress(instr) < 0){
        dcompiler.assignjmpaddress(instr, last_instr+1);
      } 
    } 
    blocks.pop();
    addresses.pop();
    diag_error_if( return_info.top()[2] > 0 && return_info.top()[2] < return_info.top()[1] ,
        "Note all control paths in block started on line '%i' return a value.", 
        return_info.top()[0] );
    return_info.pop();
    return true;
  } 
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procBREAK() { localize_script_info( (*this) );
    // only process this keyword as a breakpoint setter if it is the first word in the line
    // and there is a statement following it: i.e., break x=x+1; but not for break;
    // if the keyword is isolated, we interpret this as a breakif true; statement requesting
    // control to exit a loop if the control is within one.	
    if(pos < epos){ // break expr ... (debug expr)
      addbreakpoint = true; // process the rest of the statement
    }else{ // break; --> break out of a loop
      const string & keyword = * (ptr_keyword);
      diag_error_if(blocks.size() <= 0 || ( blocks.top() == notype ),"Unexpected '%s' outside of 'loop', 'if', or 'for'.", keyword.c_str());
      expr.clear(); 
      expr.push_back( "true" );
      last_instr = dcompiler.addinstruction(expr, false, true, runonce, false); // jmp address will be updated by endloop or endif statement
      dcompiler.addinstructiondebuginfo(tokens[spos].namelist_number,tokens[spos].line_number, tokens[epos].line_number);
      addresses.top().push_back(last_instr);
      diag_error_if(last_instr<0, "Error in '%s' statement.", keyword.c_str());
      return_info.top()[1]++; 
    }
    return true;
  } 
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procTRAP() { localize_script_info( (*this) );
    last_instr = dcompiler.addtrapinstruction();
    call_debug_trap();
    return true; // special trap instruction for debugging
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procTHEN() {
    // this keyword can appear any where at the beginning of a line
    return true;
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procIF() { localize_script_info( (*this) );
    const string & keyword = * (ptr_keyword);
    pos++; // march past current position
    diag_error_if(pos > epos, "Expected statement after %s on line '%i'.", keyword.c_str(), tokens[pos-1].line_number);
    int epos2 = epos;
    if(tokens[epos].word == "then") epos2 = epos - 1;
    preprocessor::combinetokens(tokens, expr, pos, epos2);
    last_instr = dcompiler.addinstruction(expr, true, false, runonce, false);
    dcompiler.addinstructiondebuginfo(tokens[spos].namelist_number,tokens[spos].line_number, tokens[epos2].line_number);
    diag_error_if(last_instr<0, "Error in '%s' statement end on line '%i'.", keyword.c_str(), tokens[epos].line_number);
    vector<int> ifaddresses;
    ifaddresses.push_back( last_instr ); 
    blocks.push(ifblock);
    dcompiler.push_scope(); 
    addresses.push(ifaddresses);
    return_info.push( triad( tokens[spos].line_number, 1, 0 ) ); 
    pos=epos;
    return true;
  }
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool procEXPR() { localize_script_info( (*this) );
    preprocessor::combinetokens(tokens, expr, pos,  epos);
    // if we are in a for block declaration
    if(expr.size() > 0){
      // for have special syntax which allows them to span multiple statements
      if(blocks.size() > 0 && blocks.top() == forblock && addresses.top().size() == 0){
        // we expect a conditional statement
        last_instr = dcompiler.addinstruction(expr, true, false, runonce, false);
        dcompiler.addinstructiondebuginfo(tokens[pos].namelist_number,tokens[pos].line_number, tokens[epos].line_number);
        diag_error_if(last_instr<0, "Error in conditional statement of 'for' loop.");
        // conditional has for if(true) then execute for block
        addresses.top().push_back(last_instr); // conditional test
        last_instr = dcompiler.addjmpinstruction(runonce);
        addresses.top().push_back(last_instr); // jmp beyond incrementor
        addresses.top().push_back(last_instr+1); // address of incrementor; beginning of loop block; or done statement
        // next we expect an incrementor
      }else{
        last_instr = dcompiler.addinstruction(expr, false, false, runonce, false);
        dcompiler.addinstructiondebuginfo(tokens[pos].namelist_number,tokens[pos].line_number, tokens[epos].line_number);
        diag_error_if(last_instr<0, "Error in statement or expression.");
      }
    }
    pos = epos;
    return true;
  }
  public:
  //-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - 
  bool processToken(const string & keyword)
  {
    this->ptr_keyword = & keyword;   
    if( matchkeyword(keyword,"EXPLICIT")){ procIMPLICIT(false); }
    if( matchkeyword(keyword,"IMPLICIT")){ procIMPLICIT(true); }
    if( matchkeyword(keyword,"RUNONCE", "INIT")){ return procRUNONCE(true); }
    if( matchkeyword(keyword,"ENDRUNONCE", "ENDINIT")){ return procRUNONCE(false); }
    if( matchkeyword(keyword,"if", "If")){ return procIF(); } 
    if( matchkeyword(keyword,"else", "Else")){ return procELSE(); }
    if( matchkeyword(keyword,"elseif", "ElseIf")){ return procELSEIF() ;  }
    if( matchkeyword(keyword,"endif", "EndIf")){ return procENDIF(); }
    if( matchkeyword(keyword,"_trap_")){ return procTRAP(); }
    if( matchkeyword(keyword,"then", "Then")){ return procTHEN(); }
    if( matchkeyword(keyword,"loop", "Loop")){ return procLOOP(); }
    if( matchkeyword(keyword,"endloop", "EndLoop")){ return procENDLOOP(); }
    if( matchkeyword(keyword,"for", "For")){ return procFOR(); } // self containing statement
    if( matchkeyword(keyword,"AllowImplicit", "ALLOWIMPLICIT")){ return procALLOWIMPLICIT(); }
    if( matchkeyword(keyword,"return", "RETURN")){ return procRETURN(); }
    if( matchkeyword(keyword, "DECLARE")){ return procDECLARE(); } 
    if( matchkeyword(keyword, "DO", "do")){ return procDO(); }
    if( matchkeyword(keyword, "DONE", "done")){ return procDONE(); }
    if( matchkeyword(keyword, "BREAKIF", "breakif")){ return procBREAKIF(); }
    if( matchkeyword(keyword, "BREAK", "break")){ return procBREAK(); }
    return this->procEXPR(); // process the rest of the statement until epos
  }
#undef fimatchreturnID 
  script_info( diag_compiler & dcomp, const vector<token> & tokens) : dcompiler_ptr(&dcomp), ptr_tokens(&tokens), 
  last_instr(-1), pos(-1), epos(-1),returns_called(0), cancelimplicit(false), 
  addbreakpoint(false), runonce(false), ptr_keyword(NULL)
  {}; 
  private : script_info(const script_info &); // no implicit copy constructor
};
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static bool process_script_statement( script_info & sdata ) 
{
  localize_script_info(sdata); 
  while( pos <= epos ){
    const string & keyword = tokens[pos].word;  
    if(! sdata.processToken(keyword)) return false;
    pos++;
    sdata.expr.clear(); // clear any lingering expressions
    if(cancelimplicit){ diagblock.setallowimplicit(false);  cancelimplicit = false; };
  }
  return true;
};
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static bool compile_script_block(diag_compiler & dcompiler, const vector<token>& tokens, const int spos, const int epos,control_info & if_data)
{
  model_class & mo = *(dcompiler.moptr);
  const bool isfunction = dcompiler.diagblock->isblockfunction();

  script_info script_data( dcompiler, tokens );
  script_data.epos = epos;

  int & pos = ( script_data.pos = spos );

  if( isfunction ){
    script_data.return_info.push(triad(0,1,0)); 
  }
  bool & addbreakpoint = script_data.addbreakpoint;
  bool & runonce = script_data.runonce; // runonce directive is used to run only at initialization 
  dcompiler.push_scope(); // create outer scope ....

  while(pos <= epos){
    //const char * namelist = getnamelistname( tokens[pos].namelist_number ).c_str();
    const string & keyword = tokens[pos].word;
    int line_epos = get_line_last_pos( tokens, pos, epos );
    //##########################################################################
    // if debug and breakpoint set, then display line before continuing to next
    // statement
    //##########################################################################
    bool firsttokeninline = (pos == 0) || (tokens[pos-1].line_number < tokens[pos].line_number);
    if(firsttokeninline && mo.isbreakpointon()){
      mo.mdebugger.filtercompiler(&mo, dcompiler.diagblock->getfuncname(), tokens, pos);
    }
    // this grabs the current line which is terminated by a semicolon or the end of the block
    if(line_epos >= spos){
      script_data.epos = line_epos;
      if(keyword == "breakpoint"){
        mo.setbreakpoint();
        mo.mdebugger.filtercompiler(&mo, dcompiler.diagblock->getfuncname(), tokens, pos);
        line_epos = pos;
      }else if( iskeywordcontroldirective(keyword) ){
        if( ! processcontroldirective(mo, tokens, pos, epos, if_data )){
          return false;
        }
        line_epos = pos; // pos can be adjusted across lines by the preprocessor control
      }else if(!process_script_statement(script_data)){ 
        return false;
      }
      if( addbreakpoint ){

        dcompiler.addbreakpointinstruction(runonce);
        addbreakpoint = false;
      }
    }
    pos = line_epos + 1;
    while( tokens[pos].word == ";" ) pos++;
  }
  stack<blocktype> & blocks = script_data.blocks;
  stack< vector<int> > & addresses = script_data.addresses;
  if(blocks.size() > 0 || addresses.size() > 0){
    if(blocks.top() == ifblock){
      line_error("Error: Missing 'EndIf' statement somewhere in block.\n");
    }else if(blocks.top() == loopblock){
      line_error("Error: Missing 'endloop' statement somewhere in block.\n");
    }else if(blocks.top() == runonceblock){
      line_error("Error: Missing 'endrunonce' statement somewhere in block.\n");
    }else{
      line_error("Error: Unclosed 'if','loop','for', 'endrunonce' or other block statement in block.\n");
    }
  }
  if( isfunction && script_data.returns_called <= 0 ){
    line_error("Error: Function '%s' must return a value.\n", dcompiler.diagblock->getfuncname().c_str());
  }
  dcompiler.createblock();
  dcompiler.pop_scope();
  return true;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static bool processblock(model_class & mo, const string & blockname, const vector<token> & tokens,
    const int spos, const int epos, spreprocdata & spdata, control_info & if_data){
  int pos = spos;
  while(pos <= epos){
    int line_epos = get_line_last_pos(tokens, pos, epos);
    if(line_epos >= pos){
      if(mo.isbreakpointon()){
        mo.mdebugger.filtercompiler(&mo, blockname, tokens, pos);
      }
      const string & keyword = tokens[pos].word;
      if(matchkeyword(keyword, "linear_operator", "linearop"))
      {
        if(! processlinearopline(mo, tokens, pos, line_epos, spdata, true)) return false;
      }
      else if(matchkeyword(keyword, "nonlinear_operator", "nonlinearop"))
      {
        if(! processnonlinearopline(mo, tokens, pos, line_epos) ) return false;
      }
      else if(matchkeyword(keyword,"linear_component" ,"linearcomp" ,"linear_comp"))
      {
        if(! processlinearopline(mo, tokens, pos, line_epos, spdata, false) ) return false;
      }
      else if(matchkeyword(keyword,"inhomogeneous_operator" ,"inhomo_op" ,"inhomoop"))
      {
        if(! processinhomogeneousopline(mo, tokens, pos, line_epos, spdata) ) return false;
      }
      else if(matchkeyword(keyword,"dissipation_operator" ,"dissipation", "dissip_op", "dissipop"))
      {
        if( !processdissipopline(mo, tokens, pos, line_epos, spdata) ) return false;
      }
      else if(matchkeyword(keyword,"initial_data" ,"initdata"))
      {
        if( !processinitdataline(mo, tokens, pos, line_epos, spdata) ) return false;
      }
      else if(matchkeyword(keyword,"field" ,"function" ,"variable"))
      {
        if( !processblockline(mo, tokens,pos, line_epos) ) return false;
      }
      else if(matchkeyword(keyword,"fluxfield"))
      {
        if( !processfluxfieldline(mo, tokens,pos, line_epos) ) return false;
      }
      else if(matchkeyword(keyword,"derivative"))
      {
        if( !processderivline(mo, tokens, pos, line_epos) ) return false;
      }
      else if(matchkeyword(keyword,"if"))
      { 
        if( ! processdatablockcontrol( mo, tokens, pos, line_epos ) ) return false;
      }
      else if(matchkeyword(keyword,"breakpoint"))
      { 
        mo.setbreakpoint();
        fprintf(stderr, "===> USER DEFINED COMPILE BREAK POINT ===> \n");
        mo.mdebugger.filtercompiler(&mo, blockname, tokens, pos);
      }else if( iskeywordcontroldirective(keyword) ){
        int epos_arg = epos;
        if( ! processcontroldirective(mo, tokens, pos, epos_arg, if_data) ){
          line_error("Error in file '%s' on line '%i'.\n", 
              mo.getnamelistname(tokens[epos].namelist_number).c_str(), 
              tokens[pos].line_number );
        }
        line_epos = pos; 
      }
      else
      { 
        if(processdataline(mo, tokens, pos, line_epos) < 0) return false;
      }
    }
    pos = line_epos + 1;
    while( tokens[pos].word == ";" ) pos++;
  }
  return true;
}
};
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* End class compiler::local */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void compiler::compile(model_class * mo){
  const vector<token> & tokens = mo->preprocdata.processed_namelist;
  const int msize = tokens.size();
  local::control_info if_data;
  int pos = 0;
  while(pos < msize){
    //##########################################################################
    // if debug and breakpoint set, then display line before continuing to next
    // statement
    //##########################################################################
    const char * namelist = mo->getnamelistname( tokens[pos].namelist_number ).c_str();
    //bool firsttokeninline = (pos == 0) || (tokens[pos-1].line_number < tokens[pos].line_number);
    //if(firsttokeninline){
    const string & keyword = tokens[pos].word;
    if( matchkeyword( keyword, "PSYSTEM", "DATA", "SSYSTEM") ){ // data block
      if(pos+1 >= msize || tokens[pos+1].word != "{"){
        fprintf(stderr, "Error: Keyword '%s' must be be followed by a '{'.\n", keyword.c_str());
        local::thrownamelisterror("Error in namelist '%s' on line '%i'.", namelist, tokens[pos].line_number);
      }
      int opos = pos+1;
      int cpos = local::find_closing_brace(tokens, pos+1, "{", "}");
      if(cpos < 0){ 
        fprintf(stderr, "Error: '%s' block initiated on line '%i' is missing closing '}'\n",
            keyword.c_str(), tokens[opos].line_number);
        local::thrownamelisterror("Error in namelist '%s' on line '%i'.", namelist, tokens[opos].line_number);
      }
      bool blockprocess_status = false;
      if( ! local::processblock(*mo, keyword, tokens, pos+2, cpos-1,mo->spre_data, if_data) ){
        // block sent to the block processor
        fprintf(stderr, "Error processing '%s' block initiated on line '%i'.\n", keyword.c_str(), tokens[opos].line_number);
        local::thrownamelisterror("Error in namelist '%s' on line '%i'.", namelist, tokens[opos].line_number);
      }
      pos=cpos;
    }else if( matchkeyword(keyword, "DIAGNOSTIC", "FUNCTION", "ENDSCRIPT", "STARTSCRIPT", "STATUSSCRIPT", "SCRIPT") ){ // diag script
      int opos = pos+1;
      model_diagnostics_block * newblock = new model_diagnostics_block(*mo);
      mo->mdiags.push_back( newblock );
      compiler::local::diag_compiler dcompiler( mo, newblock );
      if(keyword == "FUNCTION"){
        vector< pair<char_type, string> > arglist;
        if(!local::processfunctiondeclare(dcompiler, tokens, pos, msize, arglist)){
          local::thrownamelisterror("Error in namelist '%s' on line '%i'.", namelist, tokens[opos].line_number);
        } 
      }
      if(pos+1 >= msize || tokens[pos+1].word != "{"){
        fprintf(stderr, "Error: Keyword '%s' must be be followed by a '{'.\n", keyword.c_str());
        local::thrownamelisterror("Error in namelist '%s' on line '%i'.", namelist, tokens[pos].line_number);
      }
      int cpos = local::find_closing_brace(tokens, pos+1, "{", "}");
      if(cpos < 0){ 
        fprintf(stderr, "Error: '%s' block initiated on line '%i' is missing closing '}'\n",keyword.c_str(), tokens[opos].line_number);
        local::thrownamelisterror("Error in namelist '%s' on line '%i'.", namelist, tokens[opos].line_number);
      }
      // add some debug attributes
      int nid = (newblock->debug_attributes.nid = tokens[opos-1].namelist_number);
      newblock->debug_attributes.slinenumber = tokens[opos-1].line_number;
      newblock->debug_attributes.elinenumber = tokens[opos-1].line_number; // initialization
      if(nid != tokens[cpos].namelist_number){
        for(int xpos = cpos; xpos >= 0; xpos -- ){
          if( tokens[xpos].namelist_number == nid ){
            newblock->debug_attributes.elinenumber = tokens[xpos].line_number;
            break;
          }
        } 
      }else{ 
        newblock->debug_attributes.elinenumber = tokens[cpos].line_number;
      }
      if(keyword == "STATUSSCRIPT") dcompiler.makestatusblock();
      if(keyword == "SCRIPT") dcompiler.makescript();
      if(keyword == "STARTSCRIPT") dcompiler.makestartblock();
      if(keyword == "ENDSCRIPT") dcompiler.makeendblock();
      if( !local::compile_script_block(dcompiler, tokens, pos+2, cpos-1, if_data) ){
        fprintf(stderr, "Error processing '%s' block initiated on line '%i'.\n", keyword.c_str(), tokens[opos].line_number);
        local::thrownamelisterror("Error in namelist '%s' on line '%i'.", namelist, tokens[opos].line_number); 
      }
      pos=cpos;
    }else if(keyword == ";"){ // just ignore semi's
    }else if(keyword == gdb_KEYWORD_PREPROC_PARAMETER){
      const int opos = pos; const int epos = tokens.size() - 1;
      if( ! local::processdeclare(*mo, tokens, pos, epos) ){
        local::thrownamelisterror("Error in file '%s' on line '%i'.", 
            mo->getnamelistname(tokens[opos].namelist_number).c_str(), 
            tokens[opos].line_number);
      }
    }else if(local::iskeywordcontroldirective(keyword)){
      const int opos = pos;
      if( ! local::processcontroldirective(*mo, tokens, pos, msize-1, if_data) ){
        local::thrownamelisterror("Error in file '%s' beginning on line '%i'.", namelist, tokens[opos].line_number);
      }
    }else{ // this isn't a valid keyword
      fprintf(stderr, "Error: '%s' is an unrecognized keyword.\n", tokens[pos].word.c_str());
      local::thrownamelisterror("Error in file '%s' on line '%i'.", namelist, tokens[pos].line_number);
    }
    pos++;	
  }
  if(if_data.size() > 0){
    fprintf(stderr, "Error: Missing '%s' for '%s' statement encountered on line '%i' of file '%s'.\n",
        gdb_KEYWORD_PREPROC_ENDIF, gdb_KEYWORD_PREPROC_IF, if_data.last_if_linenumber.top(),
        mo->getnamelistname(if_data.last_if_namelist.top()).c_str());
    local::thrownamelisterror("Error in control.\n", "unknown", -1);
  }
  }

